mirror of
https://github.com/Oxalide/vsphere-influxdb-go.git
synced 2023-10-10 11:36:51 +00:00
add vendoring with go dep
This commit is contained in:
149
vendor/github.com/vmware/govmomi/govc/importx/archive.go
generated
vendored
Normal file
149
vendor/github.com/vmware/govmomi/govc/importx/archive.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright (c) 2014-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 importx
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/vmware/govmomi/ovf"
|
||||
)
|
||||
|
||||
// ArchiveFlag doesn't register any flags;
|
||||
// only encapsulates some common archive related functionality.
|
||||
type ArchiveFlag struct {
|
||||
Archive
|
||||
}
|
||||
|
||||
func newArchiveFlag(ctx context.Context) (*ArchiveFlag, context.Context) {
|
||||
return &ArchiveFlag{}, ctx
|
||||
}
|
||||
|
||||
func (f *ArchiveFlag) Register(ctx context.Context, fs *flag.FlagSet) {
|
||||
}
|
||||
|
||||
func (f *ArchiveFlag) Process(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *ArchiveFlag) ReadOvf(fpath string) ([]byte, error) {
|
||||
r, _, err := f.Archive.Open(fpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
return ioutil.ReadAll(r)
|
||||
}
|
||||
|
||||
func (f *ArchiveFlag) ReadEnvelope(fpath string) (*ovf.Envelope, error) {
|
||||
if fpath == "" {
|
||||
return &ovf.Envelope{}, nil
|
||||
}
|
||||
|
||||
r, _, err := f.Open(fpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
e, err := ovf.Unmarshal(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ovf: %s", err.Error())
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
type Archive interface {
|
||||
Open(string) (io.ReadCloser, int64, error)
|
||||
}
|
||||
|
||||
type TapeArchive struct {
|
||||
path string
|
||||
}
|
||||
|
||||
type TapeArchiveEntry struct {
|
||||
io.Reader
|
||||
f *os.File
|
||||
}
|
||||
|
||||
func (t *TapeArchiveEntry) Close() error {
|
||||
return t.f.Close()
|
||||
}
|
||||
|
||||
func (t *TapeArchive) Open(name string) (io.ReadCloser, int64, error) {
|
||||
f, err := os.Open(t.path)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
r := tar.NewReader(f)
|
||||
|
||||
for {
|
||||
h, err := r.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
matched, err := path.Match(name, path.Base(h.Name))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if matched {
|
||||
return &TapeArchiveEntry{r, f}, h.Size, nil
|
||||
}
|
||||
}
|
||||
|
||||
_ = f.Close()
|
||||
|
||||
return nil, 0, os.ErrNotExist
|
||||
}
|
||||
|
||||
type FileArchive struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (t *FileArchive) Open(name string) (io.ReadCloser, int64, error) {
|
||||
fpath := name
|
||||
if name != t.path {
|
||||
fpath = filepath.Join(filepath.Dir(t.path), name)
|
||||
}
|
||||
|
||||
s, err := os.Stat(fpath)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
f, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return f, s.Size(), nil
|
||||
}
|
91
vendor/github.com/vmware/govmomi/govc/importx/folder.go
generated
vendored
Normal file
91
vendor/github.com/vmware/govmomi/govc/importx/folder.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 importx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
|
||||
"github.com/vmware/govmomi/govc/flags"
|
||||
"github.com/vmware/govmomi/object"
|
||||
)
|
||||
|
||||
type FolderFlag struct {
|
||||
*flags.DatacenterFlag
|
||||
|
||||
folder string
|
||||
}
|
||||
|
||||
func newFolderFlag(ctx context.Context) (*FolderFlag, context.Context) {
|
||||
f := &FolderFlag{}
|
||||
f.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx)
|
||||
return f, ctx
|
||||
}
|
||||
|
||||
func (flag *FolderFlag) Register(ctx context.Context, f *flag.FlagSet) {
|
||||
flag.DatacenterFlag.Register(ctx, f)
|
||||
|
||||
f.StringVar(&flag.folder, "folder", "", "Path to folder to add the VM to")
|
||||
}
|
||||
|
||||
func (flag *FolderFlag) Process(ctx context.Context) error {
|
||||
return flag.DatacenterFlag.Process(ctx)
|
||||
}
|
||||
|
||||
func (flag *FolderFlag) Folder() (*object.Folder, error) {
|
||||
ctx := context.TODO()
|
||||
if len(flag.folder) == 0 {
|
||||
dc, err := flag.Datacenter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
folders, err := dc.Folders(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return folders.VmFolder, nil
|
||||
}
|
||||
|
||||
finder, err := flag.Finder()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mo, err := finder.ManagedObjectList(ctx, flag.folder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(mo) == 0 {
|
||||
return nil, errors.New("folder argument does not resolve to object")
|
||||
}
|
||||
if len(mo) > 1 {
|
||||
return nil, errors.New("folder argument resolves to more than one object")
|
||||
}
|
||||
|
||||
ref := mo[0].Object.Reference()
|
||||
if ref.Type != "Folder" {
|
||||
return nil, errors.New("folder argument does not resolve to folder")
|
||||
}
|
||||
|
||||
c, err := flag.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object.NewFolder(c, ref), nil
|
||||
}
|
59
vendor/github.com/vmware/govmomi/govc/importx/importable.go
generated
vendored
Normal file
59
vendor/github.com/vmware/govmomi/govc/importx/importable.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright (c) 2014 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 importx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
)
|
||||
|
||||
type importable struct {
|
||||
localPath string
|
||||
remotePath string
|
||||
}
|
||||
|
||||
func (i importable) Ext() string {
|
||||
return path.Ext(i.localPath)
|
||||
}
|
||||
|
||||
func (i importable) Base() string {
|
||||
return path.Base(i.localPath)
|
||||
}
|
||||
|
||||
func (i importable) BaseClean() string {
|
||||
b := i.Base()
|
||||
e := i.Ext()
|
||||
return b[:len(b)-len(e)]
|
||||
}
|
||||
|
||||
func (i importable) RemoteSrcVMDK() string {
|
||||
file := fmt.Sprintf("%s-src.vmdk", i.BaseClean())
|
||||
return i.toRemotePath(file)
|
||||
}
|
||||
|
||||
func (i importable) RemoteDstVMDK() string {
|
||||
file := fmt.Sprintf("%s.vmdk", i.BaseClean())
|
||||
return i.toRemotePath(file)
|
||||
}
|
||||
|
||||
func (i importable) toRemotePath(p string) string {
|
||||
if i.remotePath == "" {
|
||||
return p
|
||||
}
|
||||
|
||||
return path.Join(i.remotePath, p)
|
||||
}
|
131
vendor/github.com/vmware/govmomi/govc/importx/lease_updater.go
generated
vendored
Normal file
131
vendor/github.com/vmware/govmomi/govc/importx/lease_updater.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright (c) 2014-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 importx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/progress"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type ovfFileItem struct {
|
||||
url *url.URL
|
||||
item types.OvfFileItem
|
||||
ch chan progress.Report
|
||||
}
|
||||
|
||||
func (o ovfFileItem) Sink() chan<- progress.Report {
|
||||
return o.ch
|
||||
}
|
||||
|
||||
type leaseUpdater struct {
|
||||
client *vim25.Client
|
||||
lease *object.HttpNfcLease
|
||||
|
||||
pos int64 // Number of bytes
|
||||
total int64 // Total number of bytes
|
||||
|
||||
done chan struct{} // When lease updater should stop
|
||||
|
||||
wg sync.WaitGroup // Track when update loop is done
|
||||
}
|
||||
|
||||
func newLeaseUpdater(client *vim25.Client, lease *object.HttpNfcLease, items []ovfFileItem) *leaseUpdater {
|
||||
l := leaseUpdater{
|
||||
client: client,
|
||||
lease: lease,
|
||||
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
l.total += item.item.Size
|
||||
go l.waitForProgress(item)
|
||||
}
|
||||
|
||||
// Kickstart update loop
|
||||
l.wg.Add(1)
|
||||
go l.run()
|
||||
|
||||
return &l
|
||||
}
|
||||
|
||||
func (l *leaseUpdater) waitForProgress(item ovfFileItem) {
|
||||
var pos, total int64
|
||||
|
||||
total = item.item.Size
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-l.done:
|
||||
return
|
||||
case p, ok := <-item.ch:
|
||||
// Return in case of error
|
||||
if ok && p.Error() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
// Last element on the channel, add to total
|
||||
atomic.AddInt64(&l.pos, total-pos)
|
||||
return
|
||||
}
|
||||
|
||||
// Approximate progress in number of bytes
|
||||
x := int64(float32(total) * (p.Percentage() / 100.0))
|
||||
atomic.AddInt64(&l.pos, x-pos)
|
||||
pos = x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *leaseUpdater) run() {
|
||||
defer l.wg.Done()
|
||||
|
||||
tick := time.NewTicker(2 * time.Second)
|
||||
defer tick.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-l.done:
|
||||
return
|
||||
case <-tick.C:
|
||||
// From the vim api HttpNfcLeaseProgress(percent) doc, percent ==
|
||||
// "Completion status represented as an integer in the 0-100 range."
|
||||
// Always report the current value of percent, as it will renew the
|
||||
// lease even if the value hasn't changed or is 0.
|
||||
percent := int32(float32(100*atomic.LoadInt64(&l.pos)) / float32(l.total))
|
||||
err := l.lease.HttpNfcLeaseProgress(context.TODO(), percent)
|
||||
if err != nil {
|
||||
fmt.Printf("from lease updater: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *leaseUpdater) Done() {
|
||||
close(l.done)
|
||||
l.wg.Wait()
|
||||
}
|
95
vendor/github.com/vmware/govmomi/govc/importx/options.go
generated
vendored
Normal file
95
vendor/github.com/vmware/govmomi/govc/importx/options.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 importx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/vmware/govmomi/ovf"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type Property struct {
|
||||
types.KeyValue
|
||||
Spec *ovf.Property `json:",omitempty"`
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
Name string
|
||||
Network string
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
AllDeploymentOptions []string `json:",omitempty"`
|
||||
Deployment string
|
||||
|
||||
AllDiskProvisioningOptions []string `json:",omitempty"`
|
||||
DiskProvisioning string
|
||||
|
||||
AllIPAllocationPolicyOptions []string `json:",omitempty"`
|
||||
IPAllocationPolicy string
|
||||
|
||||
AllIPProtocolOptions []string `json:",omitempty"`
|
||||
IPProtocol string
|
||||
|
||||
PropertyMapping []Property `json:",omitempty"`
|
||||
|
||||
NetworkMapping []Network `json:",omitempty"`
|
||||
|
||||
Annotation string `json:",omitempty"`
|
||||
|
||||
PowerOn bool
|
||||
InjectOvfEnv bool
|
||||
WaitForIP bool
|
||||
Name *string
|
||||
}
|
||||
|
||||
type OptionsFlag struct {
|
||||
Options Options
|
||||
|
||||
path string
|
||||
}
|
||||
|
||||
func newOptionsFlag(ctx context.Context) (*OptionsFlag, context.Context) {
|
||||
return &OptionsFlag{}, ctx
|
||||
}
|
||||
|
||||
func (flag *OptionsFlag) Register(ctx context.Context, f *flag.FlagSet) {
|
||||
f.StringVar(&flag.path, "options", "", "Options spec file path for VM deployment")
|
||||
}
|
||||
|
||||
func (flag *OptionsFlag) Process(ctx context.Context) error {
|
||||
if len(flag.path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
in := os.Stdin
|
||||
|
||||
if flag.path != "-" {
|
||||
in, err = os.Open(flag.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
}
|
||||
|
||||
return json.NewDecoder(in).Decode(&flag.Options)
|
||||
}
|
60
vendor/github.com/vmware/govmomi/govc/importx/ova.go
generated
vendored
Normal file
60
vendor/github.com/vmware/govmomi/govc/importx/ova.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright (c) 2014-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 importx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
|
||||
"github.com/vmware/govmomi/govc/cli"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type ova struct {
|
||||
*ovfx
|
||||
}
|
||||
|
||||
func init() {
|
||||
cli.Register("import.ova", &ova{&ovfx{}})
|
||||
}
|
||||
|
||||
func (cmd *ova) Usage() string {
|
||||
return "PATH_TO_OVA"
|
||||
}
|
||||
|
||||
func (cmd *ova) Run(ctx context.Context, f *flag.FlagSet) error {
|
||||
fpath, err := cmd.Prepare(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Archive = &TapeArchive{fpath}
|
||||
|
||||
moref, err := cmd.Import(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vm := object.NewVirtualMachine(cmd.Client, *moref)
|
||||
return cmd.Deploy(vm)
|
||||
}
|
||||
|
||||
func (cmd *ova) Import(fpath string) (*types.ManagedObjectReference, error) {
|
||||
ovf := "*.ovf"
|
||||
return cmd.ovfx.Import(ovf)
|
||||
}
|
452
vendor/github.com/vmware/govmomi/govc/importx/ovf.go
generated
vendored
Normal file
452
vendor/github.com/vmware/govmomi/govc/importx/ovf.go
generated
vendored
Normal file
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
Copyright (c) 2014-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 importx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/govc/cli"
|
||||
"github.com/vmware/govmomi/govc/flags"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/ovf"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/progress"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type ovfx struct {
|
||||
*flags.DatastoreFlag
|
||||
*flags.HostSystemFlag
|
||||
*flags.OutputFlag
|
||||
*flags.ResourcePoolFlag
|
||||
|
||||
*ArchiveFlag
|
||||
*OptionsFlag
|
||||
*FolderFlag
|
||||
|
||||
Name string
|
||||
|
||||
Client *vim25.Client
|
||||
Datacenter *object.Datacenter
|
||||
Datastore *object.Datastore
|
||||
ResourcePool *object.ResourcePool
|
||||
}
|
||||
|
||||
func init() {
|
||||
cli.Register("import.ovf", &ovfx{})
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Register(ctx context.Context, f *flag.FlagSet) {
|
||||
cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx)
|
||||
cmd.DatastoreFlag.Register(ctx, f)
|
||||
cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx)
|
||||
cmd.HostSystemFlag.Register(ctx, f)
|
||||
cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
|
||||
cmd.OutputFlag.Register(ctx, f)
|
||||
cmd.ResourcePoolFlag, ctx = flags.NewResourcePoolFlag(ctx)
|
||||
cmd.ResourcePoolFlag.Register(ctx, f)
|
||||
|
||||
cmd.ArchiveFlag, ctx = newArchiveFlag(ctx)
|
||||
cmd.ArchiveFlag.Register(ctx, f)
|
||||
cmd.OptionsFlag, ctx = newOptionsFlag(ctx)
|
||||
cmd.OptionsFlag.Register(ctx, f)
|
||||
cmd.FolderFlag, ctx = newFolderFlag(ctx)
|
||||
cmd.FolderFlag.Register(ctx, f)
|
||||
|
||||
f.StringVar(&cmd.Name, "name", "", "Name to use for new entity")
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Process(ctx context.Context) error {
|
||||
if err := cmd.DatastoreFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.HostSystemFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.OutputFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.ResourcePoolFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.ArchiveFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.OptionsFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.FolderFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Usage() string {
|
||||
return "PATH_TO_OVF"
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Run(ctx context.Context, f *flag.FlagSet) error {
|
||||
fpath, err := cmd.Prepare(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Archive = &FileArchive{fpath}
|
||||
|
||||
moref, err := cmd.Import(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vm := object.NewVirtualMachine(cmd.Client, *moref)
|
||||
return cmd.Deploy(vm)
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Prepare(f *flag.FlagSet) (string, error) {
|
||||
var err error
|
||||
|
||||
args := f.Args()
|
||||
if len(args) != 1 {
|
||||
return "", errors.New("no file specified")
|
||||
}
|
||||
|
||||
cmd.Client, err = cmd.DatastoreFlag.Client()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cmd.Datacenter, err = cmd.DatastoreFlag.Datacenter()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cmd.Datastore, err = cmd.DatastoreFlag.Datastore()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return f.Arg(0), nil
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Deploy(vm *object.VirtualMachine) error {
|
||||
if err := cmd.InjectOvfEnv(vm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmd.PowerOn(vm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cmd.WaitForIP(vm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Map(op []Property) (p []types.KeyValue) {
|
||||
for _, v := range op {
|
||||
p = append(p, v.KeyValue)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cmd *ovfx) NetworkMap(e *ovf.Envelope) (p []types.OvfNetworkMapping) {
|
||||
ctx := context.TODO()
|
||||
finder, err := cmd.DatastoreFlag.Finder()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
networks := map[string]string{}
|
||||
|
||||
if e.Network != nil {
|
||||
for _, net := range e.Network.Networks {
|
||||
networks[net.Name] = net.Name
|
||||
}
|
||||
}
|
||||
|
||||
for _, net := range cmd.Options.NetworkMapping {
|
||||
networks[net.Name] = net.Network
|
||||
}
|
||||
|
||||
for src, dst := range networks {
|
||||
if net, err := finder.Network(ctx, dst); err == nil {
|
||||
p = append(p, types.OvfNetworkMapping{
|
||||
Name: src,
|
||||
Network: net.Reference(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Import(fpath string) (*types.ManagedObjectReference, error) {
|
||||
ctx := context.TODO()
|
||||
o, err := cmd.ReadOvf(fpath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e, err := cmd.ReadEnvelope(fpath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse ovf: %s", err.Error())
|
||||
}
|
||||
|
||||
name := "Govc Virtual Appliance"
|
||||
if e.VirtualSystem != nil {
|
||||
name = e.VirtualSystem.ID
|
||||
if e.VirtualSystem.Name != nil {
|
||||
name = *e.VirtualSystem.Name
|
||||
}
|
||||
}
|
||||
|
||||
// Override name from options if specified
|
||||
if cmd.Options.Name != nil {
|
||||
name = *cmd.Options.Name
|
||||
}
|
||||
|
||||
// Override name from arguments if specified
|
||||
if cmd.Name != "" {
|
||||
name = cmd.Name
|
||||
}
|
||||
|
||||
cisp := types.OvfCreateImportSpecParams{
|
||||
DiskProvisioning: cmd.Options.DiskProvisioning,
|
||||
EntityName: name,
|
||||
IpAllocationPolicy: cmd.Options.IPAllocationPolicy,
|
||||
IpProtocol: cmd.Options.IPProtocol,
|
||||
OvfManagerCommonParams: types.OvfManagerCommonParams{
|
||||
DeploymentOption: cmd.Options.Deployment,
|
||||
Locale: "US"},
|
||||
PropertyMapping: cmd.Map(cmd.Options.PropertyMapping),
|
||||
NetworkMapping: cmd.NetworkMap(e),
|
||||
}
|
||||
|
||||
m := object.NewOvfManager(cmd.Client)
|
||||
spec, err := m.CreateImportSpec(ctx, string(o), cmd.ResourcePool, cmd.Datastore, cisp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if spec.Error != nil {
|
||||
return nil, errors.New(spec.Error[0].LocalizedMessage)
|
||||
}
|
||||
if spec.Warning != nil {
|
||||
for _, w := range spec.Warning {
|
||||
_, _ = cmd.Log(fmt.Sprintf("Warning: %s\n", w.LocalizedMessage))
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.Options.Annotation != "" {
|
||||
switch s := spec.ImportSpec.(type) {
|
||||
case *types.VirtualMachineImportSpec:
|
||||
s.ConfigSpec.Annotation = cmd.Options.Annotation
|
||||
case *types.VirtualAppImportSpec:
|
||||
s.VAppConfigSpec.Annotation = cmd.Options.Annotation
|
||||
}
|
||||
}
|
||||
|
||||
var host *object.HostSystem
|
||||
if cmd.SearchFlag.IsSet() {
|
||||
if host, err = cmd.HostSystem(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
folder, err := cmd.Folder()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lease, err := cmd.ResourcePool.ImportVApp(ctx, spec.ImportSpec, folder, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := lease.Wait(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build slice of items and URLs first, so that the lease updater can know
|
||||
// about every item that needs to be uploaded, and thereby infer progress.
|
||||
var items []ovfFileItem
|
||||
|
||||
for _, device := range info.DeviceUrl {
|
||||
for _, item := range spec.FileItem {
|
||||
if device.ImportKey != item.DeviceId {
|
||||
continue
|
||||
}
|
||||
|
||||
u, err := cmd.Client.ParseURL(device.Url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i := ovfFileItem{
|
||||
url: u,
|
||||
item: item,
|
||||
ch: make(chan progress.Report),
|
||||
}
|
||||
|
||||
items = append(items, i)
|
||||
}
|
||||
}
|
||||
|
||||
u := newLeaseUpdater(cmd.Client, lease, items)
|
||||
defer u.Done()
|
||||
|
||||
for _, i := range items {
|
||||
err = cmd.Upload(lease, i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &info.Entity, lease.HttpNfcLeaseComplete(ctx)
|
||||
}
|
||||
|
||||
func (cmd *ovfx) Upload(lease *object.HttpNfcLease, ofi ovfFileItem) error {
|
||||
item := ofi.item
|
||||
file := item.Path
|
||||
|
||||
f, size, err := cmd.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
logger := cmd.ProgressLogger(fmt.Sprintf("Uploading %s... ", path.Base(file)))
|
||||
defer logger.Wait()
|
||||
|
||||
opts := soap.Upload{
|
||||
ContentLength: size,
|
||||
Progress: progress.Tee(ofi, logger),
|
||||
}
|
||||
|
||||
// Non-disk files (such as .iso) use the PUT method.
|
||||
// Overwrite: t header is also required in this case (ovftool does the same)
|
||||
if item.Create {
|
||||
opts.Method = "PUT"
|
||||
opts.Headers = map[string]string{
|
||||
"Overwrite": "t",
|
||||
}
|
||||
} else {
|
||||
opts.Method = "POST"
|
||||
opts.Type = "application/x-vnd.vmware-streamVmdk"
|
||||
}
|
||||
|
||||
return cmd.Client.Client.Upload(f, ofi.url, &opts)
|
||||
}
|
||||
|
||||
func (cmd *ovfx) PowerOn(vm *object.VirtualMachine) error {
|
||||
ctx := context.TODO()
|
||||
if !cmd.Options.PowerOn {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Log("Powering on VM...\n")
|
||||
|
||||
task, err := vm.PowerOn(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = task.WaitForResult(ctx, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *ovfx) InjectOvfEnv(vm *object.VirtualMachine) error {
|
||||
if !cmd.Options.InjectOvfEnv {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Log("Injecting OVF environment...\n")
|
||||
|
||||
var opts []types.BaseOptionValue
|
||||
|
||||
a := cmd.Client.ServiceContent.About
|
||||
|
||||
// build up Environment in order to marshal to xml
|
||||
var props []ovf.EnvProperty
|
||||
for _, p := range cmd.Options.PropertyMapping {
|
||||
props = append(props, ovf.EnvProperty{
|
||||
Key: p.Key,
|
||||
Value: p.Value,
|
||||
})
|
||||
}
|
||||
|
||||
env := ovf.Env{
|
||||
EsxID: vm.Reference().Value,
|
||||
Platform: &ovf.PlatformSection{
|
||||
Kind: a.Name,
|
||||
Version: a.Version,
|
||||
Vendor: a.Vendor,
|
||||
Locale: "US",
|
||||
},
|
||||
Property: &ovf.PropertySection{
|
||||
Properties: props,
|
||||
},
|
||||
}
|
||||
|
||||
opts = append(opts, &types.OptionValue{
|
||||
Key: "guestinfo.ovfEnv",
|
||||
Value: env.MarshalManual(),
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
task, err := vm.Reconfigure(ctx, types.VirtualMachineConfigSpec{
|
||||
ExtraConfig: opts,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return task.Wait(ctx)
|
||||
}
|
||||
|
||||
func (cmd *ovfx) WaitForIP(vm *object.VirtualMachine) error {
|
||||
ctx := context.TODO()
|
||||
if !cmd.Options.PowerOn || !cmd.Options.WaitForIP {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Log("Waiting for IP address...\n")
|
||||
ip, err := vm.WaitForIP(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Log(fmt.Sprintf("Received IP address: %s\n", ip))
|
||||
return nil
|
||||
}
|
184
vendor/github.com/vmware/govmomi/govc/importx/spec.go
generated
vendored
Normal file
184
vendor/github.com/vmware/govmomi/govc/importx/spec.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright (c) 2015-2016 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 importx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/govc/cli"
|
||||
"github.com/vmware/govmomi/ovf"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// all possible ovf property values
|
||||
// the first element being the default value
|
||||
allDeploymentOptions = []string{"small", "medium", "large"}
|
||||
allDiskProvisioningOptions = []string{"thin", "monolithicSparse", "monolithicFlat", "twoGbMaxExtentSparse", "twoGbMaxExtentFlat", "seSparse", "eagerZeroedThick", "thick", "sparse", "flat"}
|
||||
allIPAllocationPolicyOptions = []string{"dhcpPolicy", "transientPolicy", "fixedPolicy", "fixedAllocatedPolicy"}
|
||||
allIPProtocolOptions = []string{"IPv4", "IPv6"}
|
||||
)
|
||||
|
||||
type spec struct {
|
||||
*ArchiveFlag
|
||||
|
||||
verbose bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
cli.Register("import.spec", &spec{})
|
||||
}
|
||||
|
||||
func (cmd *spec) Register(ctx context.Context, f *flag.FlagSet) {
|
||||
cmd.ArchiveFlag, ctx = newArchiveFlag(ctx)
|
||||
cmd.ArchiveFlag.Register(ctx, f)
|
||||
|
||||
f.BoolVar(&cmd.verbose, "verbose", false, "Verbose spec output")
|
||||
}
|
||||
|
||||
func (cmd *spec) Process(ctx context.Context) error {
|
||||
if err := cmd.ArchiveFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *spec) Usage() string {
|
||||
return "PATH_TO_OVF_OR_OVA"
|
||||
}
|
||||
|
||||
func (cmd *spec) Run(ctx context.Context, f *flag.FlagSet) error {
|
||||
fpath := ""
|
||||
args := f.Args()
|
||||
if len(args) == 1 {
|
||||
fpath = f.Arg(0)
|
||||
}
|
||||
|
||||
if len(fpath) > 0 {
|
||||
switch path.Ext(fpath) {
|
||||
case ".ovf":
|
||||
cmd.Archive = &FileArchive{fpath}
|
||||
case "", ".ova":
|
||||
cmd.Archive = &TapeArchive{fpath}
|
||||
fpath = "*.ovf"
|
||||
default:
|
||||
return fmt.Errorf("invalid file extension %s", path.Ext(fpath))
|
||||
}
|
||||
}
|
||||
|
||||
return cmd.Spec(fpath)
|
||||
}
|
||||
|
||||
func (cmd *spec) Map(e *ovf.Envelope) (res []Property) {
|
||||
if e == nil || e.VirtualSystem == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, p := range e.VirtualSystem.Product {
|
||||
for i, v := range p.Property {
|
||||
d := ""
|
||||
if v.Default != nil {
|
||||
d = *v.Default
|
||||
}
|
||||
|
||||
// From OVF spec, section 9.5.1:
|
||||
// key-value-env = [class-value "."] key-value-prod ["." instance-value]
|
||||
k := v.Key
|
||||
if p.Class != nil {
|
||||
k = fmt.Sprintf("%s.%s", *p.Class, k)
|
||||
}
|
||||
if p.Instance != nil {
|
||||
k = fmt.Sprintf("%s.%s", k, *p.Instance)
|
||||
}
|
||||
|
||||
np := Property{KeyValue: types.KeyValue{Key: k, Value: d}}
|
||||
if cmd.verbose {
|
||||
np.Spec = &p.Property[i]
|
||||
}
|
||||
|
||||
res = append(res, np)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cmd *spec) Spec(fpath string) error {
|
||||
e, err := cmd.ReadEnvelope(fpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var deploymentOptions = allDeploymentOptions
|
||||
if e.DeploymentOption != nil && e.DeploymentOption.Configuration != nil {
|
||||
deploymentOptions = nil
|
||||
|
||||
// add default first
|
||||
for _, c := range e.DeploymentOption.Configuration {
|
||||
if c.Default != nil && *c.Default {
|
||||
deploymentOptions = append(deploymentOptions, c.ID)
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range e.DeploymentOption.Configuration {
|
||||
if c.Default == nil || !*c.Default {
|
||||
deploymentOptions = append(deploymentOptions, c.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
o := Options{
|
||||
Deployment: deploymentOptions[0],
|
||||
DiskProvisioning: allDiskProvisioningOptions[0],
|
||||
IPAllocationPolicy: allIPAllocationPolicyOptions[0],
|
||||
IPProtocol: allIPProtocolOptions[0],
|
||||
PowerOn: false,
|
||||
WaitForIP: false,
|
||||
InjectOvfEnv: false,
|
||||
PropertyMapping: cmd.Map(e)}
|
||||
|
||||
if e.VirtualSystem != nil && e.VirtualSystem.Annotation != nil {
|
||||
for _, a := range e.VirtualSystem.Annotation {
|
||||
o.Annotation += a.Annotation
|
||||
}
|
||||
}
|
||||
|
||||
if e.Network != nil {
|
||||
for _, net := range e.Network.Networks {
|
||||
o.NetworkMapping = append(o.NetworkMapping, Network{net.Name, ""})
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.verbose {
|
||||
o.AllDeploymentOptions = deploymentOptions
|
||||
o.AllDiskProvisioningOptions = allDiskProvisioningOptions
|
||||
o.AllIPAllocationPolicyOptions = allIPAllocationPolicyOptions
|
||||
o.AllIPProtocolOptions = allIPProtocolOptions
|
||||
}
|
||||
|
||||
j, err := json.Marshal(&o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(j))
|
||||
return nil
|
||||
}
|
504
vendor/github.com/vmware/govmomi/govc/importx/vmdk.go
generated
vendored
Normal file
504
vendor/github.com/vmware/govmomi/govc/importx/vmdk.go
generated
vendored
Normal file
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
Copyright (c) 2014-2016 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 importx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
||||
"github.com/vmware/govmomi/govc/cli"
|
||||
"github.com/vmware/govmomi/govc/flags"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/property"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/progress"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type vmdk struct {
|
||||
*flags.DatastoreFlag
|
||||
*flags.ResourcePoolFlag
|
||||
*flags.OutputFlag
|
||||
|
||||
upload bool
|
||||
force bool
|
||||
keep bool
|
||||
|
||||
Client *vim25.Client
|
||||
Datacenter *object.Datacenter
|
||||
Datastore *object.Datastore
|
||||
ResourcePool *object.ResourcePool
|
||||
}
|
||||
|
||||
func init() {
|
||||
cli.Register("import.vmdk", &vmdk{})
|
||||
cli.Alias("import.vmdk", "datastore.import")
|
||||
}
|
||||
|
||||
func (cmd *vmdk) Register(ctx context.Context, f *flag.FlagSet) {
|
||||
cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx)
|
||||
cmd.DatastoreFlag.Register(ctx, f)
|
||||
cmd.ResourcePoolFlag, ctx = flags.NewResourcePoolFlag(ctx)
|
||||
cmd.ResourcePoolFlag.Register(ctx, f)
|
||||
cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
|
||||
cmd.OutputFlag.Register(ctx, f)
|
||||
|
||||
f.BoolVar(&cmd.upload, "upload", true, "Upload specified disk")
|
||||
f.BoolVar(&cmd.force, "force", false, "Overwrite existing disk")
|
||||
f.BoolVar(&cmd.keep, "keep", false, "Keep uploaded disk after import")
|
||||
}
|
||||
|
||||
func (cmd *vmdk) Process(ctx context.Context) error {
|
||||
if err := cmd.DatastoreFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.ResourcePoolFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cmd.OutputFlag.Process(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *vmdk) Usage() string {
|
||||
return "PATH_TO_VMDK [REMOTE_DIRECTORY]"
|
||||
}
|
||||
|
||||
func (cmd *vmdk) Run(ctx context.Context, f *flag.FlagSet) error {
|
||||
var err error
|
||||
|
||||
args := f.Args()
|
||||
if len(args) < 1 {
|
||||
return errors.New("no file to import")
|
||||
}
|
||||
|
||||
file := importable{
|
||||
localPath: f.Arg(0),
|
||||
}
|
||||
|
||||
// Include remote path if specified
|
||||
if len(args) >= 2 {
|
||||
file.remotePath = f.Arg(1)
|
||||
}
|
||||
|
||||
cmd.Client, err = cmd.DatastoreFlag.Client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Datacenter, err = cmd.DatastoreFlag.Datacenter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Datastore, err = cmd.DatastoreFlag.Datastore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.PrepareDestination(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd.upload {
|
||||
err = cmd.Upload(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return cmd.Import(file)
|
||||
}
|
||||
|
||||
// PrepareDestination makes sure that the destination VMDK does not yet exist.
|
||||
// If the force flag is passed, it removes the existing VMDK. This functions
|
||||
// exists to give a meaningful error if the remote VMDK already exists.
|
||||
//
|
||||
// CopyVirtualDisk can return a "<src> file does not exist" error while in fact
|
||||
// the source file *does* exist and the *destination* file also exist.
|
||||
//
|
||||
func (cmd *vmdk) PrepareDestination(i importable) error {
|
||||
ctx := context.TODO()
|
||||
vmdkPath := i.RemoteDstVMDK()
|
||||
res, err := cmd.Datastore.Stat(ctx, vmdkPath)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case object.DatastoreNoSuchDirectoryError:
|
||||
// The base path doesn't exist. Create it.
|
||||
dsPath := cmd.Datastore.Path(path.Dir(vmdkPath))
|
||||
m := object.NewFileManager(cmd.Client)
|
||||
return m.MakeDirectory(ctx, dsPath, cmd.Datacenter, true)
|
||||
case object.DatastoreNoSuchFileError:
|
||||
// Destination path doesn't exist; all good to continue with import.
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Check that the returned entry has the right type.
|
||||
switch res.(type) {
|
||||
case *types.VmDiskFileInfo:
|
||||
default:
|
||||
expected := "VmDiskFileInfo"
|
||||
actual := reflect.TypeOf(res)
|
||||
panic(fmt.Sprintf("Expected: %s, actual: %s", expected, actual))
|
||||
}
|
||||
|
||||
if !cmd.force {
|
||||
dsPath := cmd.Datastore.Path(vmdkPath)
|
||||
err = fmt.Errorf("File %s already exists", dsPath)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete existing disk.
|
||||
err = cmd.DeleteDisk(vmdkPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *vmdk) Upload(i importable) error {
|
||||
ctx := context.TODO()
|
||||
p := soap.DefaultUpload
|
||||
if cmd.OutputFlag.TTY {
|
||||
logger := cmd.ProgressLogger("Uploading... ")
|
||||
p.Progress = logger
|
||||
defer logger.Wait()
|
||||
}
|
||||
|
||||
return cmd.Datastore.UploadFile(ctx, i.localPath, i.RemoteSrcVMDK(), &p)
|
||||
}
|
||||
|
||||
func (cmd *vmdk) Import(i importable) error {
|
||||
err := cmd.Copy(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cmd.keep {
|
||||
err = cmd.DeleteDisk(i.RemoteSrcVMDK())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *vmdk) Copy(i importable) error {
|
||||
var err error
|
||||
|
||||
logger := cmd.ProgressLogger("Importing... ")
|
||||
defer logger.Wait()
|
||||
|
||||
agg := progress.NewAggregator(logger)
|
||||
defer agg.Done()
|
||||
|
||||
switch p := cmd.Client.ServiceContent.About.ApiType; p {
|
||||
case "HostAgent":
|
||||
err = cmd.CopyHostAgent(i, agg)
|
||||
case "VirtualCenter":
|
||||
err = cmd.CopyVirtualCenter(i, agg)
|
||||
default:
|
||||
return fmt.Errorf("unsupported ApiType: %s", p)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (cmd *vmdk) CopyHostAgent(i importable, s progress.Sinker) error {
|
||||
ctx := context.TODO()
|
||||
spec := &types.VirtualDiskSpec{
|
||||
AdapterType: "lsiLogic",
|
||||
DiskType: "thin",
|
||||
}
|
||||
|
||||
dc := cmd.Datacenter
|
||||
src := cmd.Datastore.Path(i.RemoteSrcVMDK())
|
||||
dst := cmd.Datastore.Path(i.RemoteDstVMDK())
|
||||
vdm := object.NewVirtualDiskManager(cmd.Client)
|
||||
task, err := vdm.CopyVirtualDisk(ctx, src, dc, dst, dc, spec, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ps := progress.Prefix(s, "copying disk")
|
||||
_, err = task.WaitForResult(ctx, ps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *vmdk) CopyVirtualCenter(i importable, s progress.Sinker) error {
|
||||
var err error
|
||||
|
||||
srcName := i.BaseClean() + "-srcvm"
|
||||
dstName := i.BaseClean() + "-dstvm"
|
||||
|
||||
spec := &configSpec{
|
||||
Name: srcName,
|
||||
GuestId: "otherGuest",
|
||||
Files: &types.VirtualMachineFileInfo{
|
||||
VmPathName: fmt.Sprintf("[%s]", cmd.Datastore.Name()),
|
||||
},
|
||||
}
|
||||
|
||||
spec.AddDisk(cmd.Datastore, i.RemoteSrcVMDK())
|
||||
|
||||
src, err := cmd.CreateVM(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst, err := cmd.CloneVM(src, dstName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.DestroyVM(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vmdk, err := cmd.DetachDisk(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.MoveDisk(vmdk, i.RemoteDstVMDK())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.DestroyVM(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cmd *vmdk) MoveDisk(src, dst string) error {
|
||||
ctx := context.TODO()
|
||||
dsSrc := cmd.Datastore.Path(src)
|
||||
dsDst := cmd.Datastore.Path(dst)
|
||||
vdm := object.NewVirtualDiskManager(cmd.Client)
|
||||
task, err := vdm.MoveVirtualDisk(ctx, dsSrc, cmd.Datacenter, dsDst, cmd.Datacenter, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return task.Wait(ctx)
|
||||
}
|
||||
|
||||
func (cmd *vmdk) DeleteDisk(path string) error {
|
||||
ctx := context.TODO()
|
||||
vdm := object.NewVirtualDiskManager(cmd.Client)
|
||||
task, err := vdm.DeleteVirtualDisk(ctx, cmd.Datastore.Path(path), cmd.Datacenter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return task.Wait(ctx)
|
||||
}
|
||||
|
||||
func (cmd *vmdk) DetachDisk(vm *object.VirtualMachine) (string, error) {
|
||||
ctx := context.TODO()
|
||||
var mvm mo.VirtualMachine
|
||||
|
||||
pc := property.DefaultCollector(cmd.Client)
|
||||
err := pc.RetrieveOne(ctx, vm.Reference(), []string{"config.hardware"}, &mvm)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
spec := new(configSpec)
|
||||
dsFile := spec.RemoveDisk(&mvm)
|
||||
|
||||
task, err := vm.Reconfigure(ctx, spec.ToSpec())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = task.Wait(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return dsFile, nil
|
||||
}
|
||||
|
||||
func (cmd *vmdk) CreateVM(spec *configSpec) (*object.VirtualMachine, error) {
|
||||
ctx := context.TODO()
|
||||
folders, err := cmd.Datacenter.Folders(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task, err := folders.VmFolder.CreateVM(ctx, spec.ToSpec(), cmd.ResourcePool, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := task.WaitForResult(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object.NewVirtualMachine(cmd.Client, info.Result.(types.ManagedObjectReference)), nil
|
||||
}
|
||||
|
||||
func (cmd *vmdk) CloneVM(vm *object.VirtualMachine, name string) (*object.VirtualMachine, error) {
|
||||
ctx := context.TODO()
|
||||
folders, err := cmd.Datacenter.Folders(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spec := types.VirtualMachineCloneSpec{
|
||||
Config: &types.VirtualMachineConfigSpec{},
|
||||
Location: types.VirtualMachineRelocateSpec{},
|
||||
}
|
||||
|
||||
task, err := vm.Clone(ctx, folders.VmFolder, name, spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := task.WaitForResult(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object.NewVirtualMachine(cmd.Client, info.Result.(types.ManagedObjectReference)), nil
|
||||
}
|
||||
|
||||
func (cmd *vmdk) DestroyVM(vm *object.VirtualMachine) error {
|
||||
ctx := context.TODO()
|
||||
_, err := cmd.DetachDisk(vm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
task, err := vm.Destroy(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = task.Wait(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type configSpec types.VirtualMachineConfigSpec
|
||||
|
||||
func (c *configSpec) ToSpec() types.VirtualMachineConfigSpec {
|
||||
return types.VirtualMachineConfigSpec(*c)
|
||||
}
|
||||
|
||||
func (c *configSpec) AddChange(d types.BaseVirtualDeviceConfigSpec) {
|
||||
c.DeviceChange = append(c.DeviceChange, d)
|
||||
}
|
||||
|
||||
func (c *configSpec) AddDisk(ds *object.Datastore, path string) {
|
||||
var devices object.VirtualDeviceList
|
||||
|
||||
controller, err := devices.CreateSCSIController("")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
devices = append(devices, controller)
|
||||
|
||||
disk := devices.CreateDisk(controller.(types.BaseVirtualController), ds.Reference(), ds.Path(path))
|
||||
devices = append(devices, disk)
|
||||
|
||||
spec, err := devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.DeviceChange = append(c.DeviceChange, spec...)
|
||||
}
|
||||
|
||||
var dsPathRegexp = regexp.MustCompile(`^\[.*\] (.*)$`)
|
||||
|
||||
func (c *configSpec) RemoveDisk(vm *mo.VirtualMachine) string {
|
||||
var file string
|
||||
|
||||
for _, d := range vm.Config.Hardware.Device {
|
||||
switch device := d.(type) {
|
||||
case *types.VirtualDisk:
|
||||
if file != "" {
|
||||
panic("expected VM to have only one disk")
|
||||
}
|
||||
|
||||
switch backing := device.Backing.(type) {
|
||||
case *types.VirtualDiskFlatVer1BackingInfo:
|
||||
file = backing.FileName
|
||||
case *types.VirtualDiskFlatVer2BackingInfo:
|
||||
file = backing.FileName
|
||||
case *types.VirtualDiskSeSparseBackingInfo:
|
||||
file = backing.FileName
|
||||
case *types.VirtualDiskSparseVer1BackingInfo:
|
||||
file = backing.FileName
|
||||
case *types.VirtualDiskSparseVer2BackingInfo:
|
||||
file = backing.FileName
|
||||
default:
|
||||
name := reflect.TypeOf(device.Backing).String()
|
||||
panic(fmt.Sprintf("unexpected backing type: %s", name))
|
||||
}
|
||||
|
||||
// Remove [datastore] prefix
|
||||
m := dsPathRegexp.FindStringSubmatch(file)
|
||||
if len(m) != 2 {
|
||||
panic(fmt.Sprintf("expected regexp match for %#v", file))
|
||||
}
|
||||
file = m[1]
|
||||
|
||||
removeOp := &types.VirtualDeviceConfigSpec{
|
||||
Operation: types.VirtualDeviceConfigSpecOperationRemove,
|
||||
Device: device,
|
||||
}
|
||||
|
||||
c.AddChange(removeOp)
|
||||
}
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
Reference in New Issue
Block a user