tests: upload & test in vCenter. Closes #338

This commit is contained in:
Alexander Todorov 2020-07-30 09:26:13 -04:00 committed by Tom Gundersen
parent 02346faff8
commit 9cce43d384
255 changed files with 127290 additions and 3 deletions

View file

@ -0,0 +1,187 @@
/*
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"
"bytes"
"context"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/soap"
)
// 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.Open(fpath)
if err != nil {
return nil, err
}
defer r.Close()
return ioutil.ReadAll(r)
}
func (f *ArchiveFlag) ReadEnvelope(data []byte) (*ovf.Envelope, error) {
e, err := ovf.Unmarshal(bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("failed to parse ovf: %s", err)
}
return e, nil
}
type Archive interface {
Open(string) (io.ReadCloser, int64, error)
}
type TapeArchive struct {
Path string
Opener
}
type TapeArchiveEntry struct {
io.Reader
f io.Closer
Name string
}
func (t *TapeArchiveEntry) Close() error {
return t.f.Close()
}
func (t *TapeArchive) Open(name string) (io.ReadCloser, int64, error) {
f, _, err := t.OpenFile(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.Name}, h.Size, nil
}
}
_ = f.Close()
return nil, 0, os.ErrNotExist
}
type FileArchive struct {
Path string
Opener
}
func (t *FileArchive) Open(name string) (io.ReadCloser, int64, error) {
fpath := name
if name != t.Path {
index := strings.LastIndex(t.Path, "/")
if index != -1 {
fpath = t.Path[:index] + "/" + name
}
}
return t.OpenFile(fpath)
}
type Opener struct {
*vim25.Client
}
func isRemotePath(path string) bool {
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
return true
}
return false
}
func (o Opener) OpenLocal(path string) (io.ReadCloser, int64, error) {
f, err := os.Open(filepath.Clean(path))
if err != nil {
return nil, 0, err
}
s, err := f.Stat()
if err != nil {
return nil, 0, err
}
return f, s.Size(), nil
}
func (o Opener) OpenFile(path string) (io.ReadCloser, int64, error) {
if isRemotePath(path) {
return o.OpenRemote(path)
}
return o.OpenLocal(path)
}
func (o Opener) OpenRemote(link string) (io.ReadCloser, int64, error) {
if o.Client == nil {
return nil, 0, errors.New("remote path not supported")
}
u, err := url.Parse(link)
if err != nil {
return nil, 0, err
}
return o.Download(context.Background(), u, &soap.DefaultDownload)
}

View 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)
}

View file

@ -0,0 +1,205 @@
/*
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"
"fmt"
"os"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/object"
"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 `json:",omitempty"`
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"`
MarkAsTemplate bool
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)
}
func (flag *OptionsFlag) powerOn(vm *object.VirtualMachine, out *flags.OutputFlag) error {
if !flag.Options.PowerOn || flag.Options.MarkAsTemplate {
return nil
}
out.Log("Powering on VM...\n")
task, err := vm.PowerOn(context.Background())
if err != nil {
return err
}
return task.Wait(context.Background())
}
func (flag *OptionsFlag) markAsTemplate(vm *object.VirtualMachine, out *flags.OutputFlag) error {
if !flag.Options.MarkAsTemplate {
return nil
}
out.Log("Marking VM as template...\n")
return vm.MarkAsTemplate(context.Background())
}
func (flag *OptionsFlag) injectOvfEnv(vm *object.VirtualMachine, out *flags.OutputFlag) error {
if !flag.Options.InjectOvfEnv {
return nil
}
out.Log("Injecting OVF environment...\n")
var opts []types.BaseOptionValue
a := vm.Client().ServiceContent.About
// build up Environment in order to marshal to xml
var props []ovf.EnvProperty
for _, p := range flag.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(),
})
task, err := vm.Reconfigure(context.Background(), types.VirtualMachineConfigSpec{
ExtraConfig: opts,
})
if err != nil {
return err
}
return task.Wait(context.Background())
}
func (flag *OptionsFlag) waitForIP(vm *object.VirtualMachine, out *flags.OutputFlag) error {
if !flag.Options.PowerOn || !flag.Options.WaitForIP || flag.Options.MarkAsTemplate {
return nil
}
out.Log("Waiting for IP address...\n")
ip, err := vm.WaitForIP(context.Background())
if err != nil {
return err
}
out.Log(fmt.Sprintf("Received IP address: %s\n", ip))
return nil
}
func (flag *OptionsFlag) Deploy(vm *object.VirtualMachine, out *flags.OutputFlag) error {
deploy := []func(*object.VirtualMachine, *flags.OutputFlag) error{
flag.injectOvfEnv,
flag.markAsTemplate,
flag.powerOn,
flag.waitForIP,
}
for _, step := range deploy {
if err := step(vm, out); err != nil {
return err
}
}
return nil
}

63
vendor/github.com/vmware/govmomi/govc/importx/ova.go generated vendored Normal file
View file

@ -0,0 +1,63 @@
/*
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
}
archive := &TapeArchive{Path: fpath}
archive.Client = cmd.Client
cmd.Archive = archive
moref, err := cmd.Import(fpath)
if err != nil {
return err
}
vm := object.NewVirtualMachine(cmd.Client, *moref)
return cmd.Deploy(vm, cmd.OutputFlag)
}
func (cmd *ova) Import(fpath string) (*types.ManagedObjectReference, error) {
ovf := "*.ovf"
return cmd.ovfx.Import(ovf)
}

343
vendor/github.com/vmware/govmomi/govc/importx/ovf.go generated vendored Normal file
View file

@ -0,0 +1,343 @@
/*
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/find"
"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type ovfx struct {
*flags.DatastoreFlag
*flags.HostSystemFlag
*flags.OutputFlag
*flags.ResourcePoolFlag
*flags.FolderFlag
*ArchiveFlag
*OptionsFlag
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.FolderFlag, ctx = flags.NewFolderFlag(ctx)
cmd.FolderFlag.Register(ctx, f)
cmd.ArchiveFlag, ctx = newArchiveFlag(ctx)
cmd.ArchiveFlag.Register(ctx, f)
cmd.OptionsFlag, ctx = newOptionsFlag(ctx)
cmd.OptionsFlag.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
}
archive := &FileArchive{Path: fpath}
archive.Client = cmd.Client
cmd.Archive = archive
moref, err := cmd.Import(fpath)
if err != nil {
return err
}
vm := object.NewVirtualMachine(cmd.Client, *moref)
return cmd.Deploy(vm, cmd.OutputFlag)
}
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.ResourcePoolIfSpecified()
if err != nil {
return "", err
}
return f.Arg(0), 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) ([]types.OvfNetworkMapping, error) {
ctx := context.TODO()
finder, err := cmd.DatastoreFlag.Finder()
if err != nil {
return nil, err
}
var nmap []types.OvfNetworkMapping
for _, m := range cmd.Options.NetworkMapping {
if m.Network == "" {
continue // Not set, let vSphere choose the default network
}
var ref types.ManagedObjectReference
net, err := finder.Network(ctx, m.Network)
if err != nil {
switch err.(type) {
case *find.NotFoundError:
if !ref.FromString(m.Network) {
return nil, err
} // else this is a raw MO ref
default:
return nil, err
}
} else {
ref = net.Reference()
}
nmap = append(nmap, types.OvfNetworkMapping{
Name: m.Name,
Network: ref,
})
}
return nmap, err
}
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(o)
if err != nil {
return nil, fmt.Errorf("failed to parse ovf: %s", err)
}
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
}
nmap, err := cmd.NetworkMap(e)
if err != nil {
return nil, err
}
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: nmap,
}
host, err := cmd.HostSystemIfSpecified()
if err != nil {
return nil, err
}
if cmd.ResourcePool == nil {
if host == nil {
cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool()
} else {
cmd.ResourcePool, err = host.ResourcePool(ctx)
}
if err != nil {
return nil, err
}
}
m := ovf.NewManager(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 folder *object.Folder
// The folder argument must not be set on a VM in a vApp, otherwise causes
// InvalidArgument fault: A specified parameter was not correct: pool
if cmd.ResourcePool.Reference().Type != "VirtualApp" {
folder, err = cmd.FolderOrDefault("vm")
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, spec.FileItem)
if err != nil {
return nil, err
}
u := lease.StartUpdater(ctx, info)
defer u.Done()
for _, i := range info.Items {
err = cmd.Upload(ctx, lease, i)
if err != nil {
return nil, err
}
}
return &info.Entity, lease.Complete(ctx)
}
func (cmd *ovfx) Upload(ctx context.Context, lease *nfc.Lease, item nfc.FileItem) error {
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: logger,
}
return lease.Upload(ctx, item, f, opts)
}

256
vendor/github.com/vmware/govmomi/govc/importx/spec.go generated vendored Normal file
View file

@ -0,0 +1,256 @@
/*
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"
"flag"
"fmt"
"io"
"path"
"strings"
"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/vim25/types"
)
var (
allDiskProvisioningOptions = []string{
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeFlat),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeMonolithicSparse),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeMonolithicFlat),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeTwoGbMaxExtentSparse),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeTwoGbMaxExtentFlat),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeThin),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeThick),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeSeSparse),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeEagerZeroedThick),
string(types.OvfCreateImportSpecParamsDiskProvisioningTypeSparse),
}
allIPAllocationPolicyOptions = []string{
string(types.VAppIPAssignmentInfoIpAllocationPolicyDhcpPolicy),
string(types.VAppIPAssignmentInfoIpAllocationPolicyTransientPolicy),
string(types.VAppIPAssignmentInfoIpAllocationPolicyFixedPolicy),
string(types.VAppIPAssignmentInfoIpAllocationPolicyFixedAllocatedPolicy),
}
allIPProtocolOptions = []string{
string(types.VAppIPAssignmentInfoProtocolsIPv4),
string(types.VAppIPAssignmentInfoProtocolsIPv6),
}
)
type spec struct {
*ArchiveFlag
*flags.ClientFlag
*flags.OutputFlag
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)
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
cmd.ClientFlag.Register(ctx, f)
cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
cmd.OutputFlag.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
}
if err := cmd.ClientFlag.Process(ctx); err != nil {
return err
}
return cmd.OutputFlag.Process(ctx)
}
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{Path: fpath}
case "", ".ova":
cmd.Archive = &TapeArchive{Path: fpath}
fpath = "*.ovf"
default:
return fmt.Errorf("invalid file extension %s", path.Ext(fpath))
}
if isRemotePath(fpath) {
client, err := cmd.Client()
if err != nil {
return err
}
switch archive := cmd.Archive.(type) {
case *FileArchive:
archive.Client = client
case *TapeArchive:
archive.Client = client
}
}
}
env, err := cmd.Spec(fpath)
if err != nil {
return err
}
if !cmd.All() {
cmd.JSON = true
}
return cmd.WriteResult(&specResult{env})
}
type specResult struct {
*Options
}
func (*specResult) Write(w io.Writer) error {
return nil
}
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 {
if v.UserConfigurable == nil || !*v.UserConfigurable {
continue
}
d := ""
if v.Default != nil {
d = *v.Default
}
// vSphere only accept True/False as boolean values for some reason
if v.Type == "boolean" {
d = strings.Title(d)
}
// 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) (*Options, error) {
e := &ovf.Envelope{}
if fpath != "" {
d, err := cmd.ReadOvf(fpath)
if err != nil {
return nil, err
}
if e, err = cmd.ReadEnvelope(d); err != nil {
return nil, err
}
}
var deploymentOptions []string
if e.DeploymentOption != nil && e.DeploymentOption.Configuration != 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{
DiskProvisioning: allDiskProvisioningOptions[0],
IPAllocationPolicy: allIPAllocationPolicyOptions[0],
IPProtocol: allIPProtocolOptions[0],
MarkAsTemplate: false,
PowerOn: false,
WaitForIP: false,
InjectOvfEnv: false,
PropertyMapping: cmd.Map(e),
}
if deploymentOptions != nil {
o.Deployment = deploymentOptions[0]
}
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 {
if deploymentOptions != nil {
o.AllDeploymentOptions = deploymentOptions
}
o.AllDiskProvisioningOptions = allDiskProvisioningOptions
o.AllIPAllocationPolicyOptions = allIPAllocationPolicyOptions
o.AllIPProtocolOptions = allIPProtocolOptions
}
return &o, nil
}

132
vendor/github.com/vmware/govmomi/govc/importx/vmdk.go generated vendored Normal file
View file

@ -0,0 +1,132 @@
/*
Copyright (c) 2014-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 importx
import (
"context"
"errors"
"flag"
"fmt"
"path"
"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/vmdk"
)
type disk struct {
*flags.DatastoreFlag
*flags.ResourcePoolFlag
*flags.FolderFlag
*flags.OutputFlag
force bool
}
func init() {
cli.Register("import.vmdk", &disk{})
}
func (cmd *disk) 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.FolderFlag, ctx = flags.NewFolderFlag(ctx)
cmd.FolderFlag.Register(ctx, f)
cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
cmd.OutputFlag.Register(ctx, f)
f.BoolVar(&cmd.force, "force", false, "Overwrite existing disk")
}
func (cmd *disk) 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.FolderFlag.Process(ctx); err != nil {
return err
}
if err := cmd.OutputFlag.Process(ctx); err != nil {
return err
}
return nil
}
func (cmd *disk) Usage() string {
return "PATH_TO_VMDK [REMOTE_DIRECTORY]"
}
func (cmd *disk) Run(ctx context.Context, f *flag.FlagSet) error {
args := f.Args()
if len(args) < 1 {
return errors.New("no file to import")
}
src := f.Arg(0)
c, err := cmd.DatastoreFlag.Client()
if err != nil {
return err
}
dc, err := cmd.DatastoreFlag.Datacenter()
if err != nil {
return err
}
ds, err := cmd.DatastoreFlag.Datastore()
if err != nil {
return err
}
pool, err := cmd.ResourcePoolFlag.ResourcePool()
if err != nil {
return err
}
folder, err := cmd.FolderOrDefault("vm")
if err != nil {
return err
}
logger := cmd.ProgressLogger(fmt.Sprintf("Uploading %s... ", path.Base(src)))
defer logger.Wait()
p := vmdk.ImportParams{
Path: f.Arg(1),
Logger: logger,
Type: "", // TODO: flag
Force: cmd.force,
Datacenter: dc,
Pool: pool,
Folder: folder,
}
err = vmdk.Import(ctx, c, src, ds, p)
if err != nil && err == vmdk.ErrInvalidFormat {
return fmt.Errorf(`%s
The vmdk can be converted using one of:
vmware-vdiskmanager -t 5 -r '%s' new.vmdk
qemu-img convert -O vmdk -o subformat=streamOptimized '%s' new.vmdk`, err, src, src)
}
return err
}