github.com/vmware/govmomi@v0.51.0/simulator/ovf_manager.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package simulator
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"math"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/vmware/govmomi/object"
    15  	"github.com/vmware/govmomi/ovf"
    16  	"github.com/vmware/govmomi/simulator/esx"
    17  	"github.com/vmware/govmomi/vim25/methods"
    18  	"github.com/vmware/govmomi/vim25/mo"
    19  	"github.com/vmware/govmomi/vim25/soap"
    20  	"github.com/vmware/govmomi/vim25/types"
    21  )
    22  
    23  type OvfManager struct {
    24  	mo.OvfManager
    25  }
    26  
    27  func ovfDisk(e *ovf.Envelope, diskID string) *ovf.VirtualDiskDesc {
    28  	for _, disk := range e.Disk.Disks {
    29  		if strings.HasSuffix(diskID, disk.DiskID) {
    30  			return &disk
    31  		}
    32  	}
    33  	return nil
    34  }
    35  
    36  func ovfNetwork(ctx *Context, req *types.CreateImportSpec, item ovf.ResourceAllocationSettingData) types.BaseVirtualDeviceBackingInfo {
    37  	if len(item.Connection) == 0 {
    38  		return nil
    39  	}
    40  	pool := ctx.Map.Get(req.ResourcePool).(mo.Entity)
    41  	ref := ctx.Map.getEntityDatacenter(pool).defaultNetwork()[0] // Default to VM Network
    42  	c := item.Connection[0]
    43  
    44  	cisp := req.Cisp.GetOvfCreateImportSpecParams()
    45  
    46  	for _, net := range cisp.NetworkMapping {
    47  		if net.Name == c {
    48  			ref = net.Network
    49  			break
    50  		}
    51  	}
    52  
    53  	switch obj := ctx.Map.Get(ref).(type) {
    54  	case *mo.Network:
    55  		return &types.VirtualEthernetCardNetworkBackingInfo{
    56  			VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
    57  				DeviceName: obj.Name,
    58  			},
    59  		}
    60  	case *DistributedVirtualPortgroup:
    61  		dvs := ctx.Map.Get(*obj.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
    62  		return &types.VirtualEthernetCardDistributedVirtualPortBackingInfo{
    63  			Port: types.DistributedVirtualSwitchPortConnection{
    64  				PortgroupKey: obj.Key,
    65  				SwitchUuid:   dvs.Config.GetDVSConfigInfo().Uuid,
    66  			},
    67  		}
    68  	default:
    69  		log.Printf("ovf: unknown network type: %T", ref)
    70  		return nil
    71  	}
    72  }
    73  
    74  func ovfDiskCapacity(disk *ovf.VirtualDiskDesc) int64 {
    75  	b, _ := strconv.ParseUint(disk.Capacity, 10, 64)
    76  	if disk.CapacityAllocationUnits == nil {
    77  		return int64(b)
    78  	}
    79  	c := strings.Fields(*disk.CapacityAllocationUnits)
    80  	if len(c) == 3 && c[0] == "byte" && c[1] == "*" { // "byte * 2^20"
    81  		p := strings.Split(c[2], "^")
    82  		x, _ := strconv.ParseUint(p[0], 10, 64)
    83  		if len(p) == 2 {
    84  			y, _ := strconv.ParseUint(p[1], 10, 64)
    85  			b *= uint64(math.Pow(float64(x), float64(y)))
    86  		} else {
    87  			b *= x
    88  		}
    89  	}
    90  	return int64(b / 1024)
    91  }
    92  
    93  func (m *OvfManager) CreateImportSpec(ctx *Context, req *types.CreateImportSpec) soap.HasFault {
    94  	body := new(methods.CreateImportSpecBody)
    95  
    96  	env, err := ovf.Unmarshal(strings.NewReader(req.OvfDescriptor))
    97  	if err != nil {
    98  		body.Fault_ = Fault(err.Error(), &types.InvalidArgument{InvalidProperty: "ovfDescriptor"})
    99  		return body
   100  	}
   101  
   102  	cisp := req.Cisp.GetOvfCreateImportSpecParams()
   103  
   104  	ds := ctx.Map.Get(req.Datastore).(*Datastore)
   105  	path := object.DatastorePath{Datastore: ds.Name}
   106  	vapp := &types.VAppConfigSpec{}
   107  	spec := &types.VirtualMachineImportSpec{
   108  		ConfigSpec: types.VirtualMachineConfigSpec{
   109  			Name:    cisp.EntityName,
   110  			Version: esx.HardwareVersion,
   111  			GuestId: string(types.VirtualMachineGuestOsIdentifierOtherGuest),
   112  			Files: &types.VirtualMachineFileInfo{
   113  				VmPathName: path.String(),
   114  			},
   115  			NumCPUs:           1,
   116  			NumCoresPerSocket: 1,
   117  			MemoryMB:          32,
   118  			VAppConfig:        vapp,
   119  		},
   120  		ResPoolEntity: &req.ResourcePool,
   121  	}
   122  
   123  	index := 0
   124  	for i, product := range env.VirtualSystem.Product {
   125  		vapp.Product = append(vapp.Product, types.VAppProductSpec{
   126  			ArrayUpdateSpec: types.ArrayUpdateSpec{
   127  				Operation: types.ArrayUpdateOperationAdd,
   128  			},
   129  			Info: &types.VAppProductInfo{
   130  				Key:        int32(i),
   131  				ClassId:    toString(product.Class),
   132  				InstanceId: toString(product.Instance),
   133  				Name:       product.Product,
   134  				Vendor:     product.Vendor,
   135  				Version:    product.Version,
   136  			},
   137  		})
   138  
   139  		for _, p := range product.Property {
   140  			key := product.Key(p)
   141  			val := ""
   142  
   143  			for _, m := range cisp.PropertyMapping {
   144  				if m.Key == key {
   145  					val = m.Value
   146  				}
   147  			}
   148  
   149  			vapp.Property = append(vapp.Property, types.VAppPropertySpec{
   150  				ArrayUpdateSpec: types.ArrayUpdateSpec{
   151  					Operation: types.ArrayUpdateOperationAdd,
   152  				},
   153  				Info: &types.VAppPropertyInfo{
   154  					Key:              int32(index),
   155  					ClassId:          toString(product.Class),
   156  					InstanceId:       toString(product.Instance),
   157  					Id:               p.Key,
   158  					Category:         product.Category,
   159  					Label:            toString(p.Label),
   160  					Type:             p.Type,
   161  					UserConfigurable: p.UserConfigurable,
   162  					DefaultValue:     toString(p.Default),
   163  					Value:            val,
   164  					Description:      toString(p.Description),
   165  				},
   166  			})
   167  			index++
   168  		}
   169  	}
   170  
   171  	if cisp.DeploymentOption == "" && env.DeploymentOption != nil {
   172  		for _, c := range env.DeploymentOption.Configuration {
   173  			if isTrue(c.Default) {
   174  				cisp.DeploymentOption = c.ID
   175  				break
   176  			}
   177  		}
   178  	}
   179  
   180  	if os := env.VirtualSystem.OperatingSystem; os != nil {
   181  		if id := os.OSType; id != nil {
   182  			spec.ConfigSpec.GuestId = *id
   183  		}
   184  	}
   185  
   186  	var device object.VirtualDeviceList
   187  	result := types.OvfCreateImportSpecResult{
   188  		ImportSpec: spec,
   189  	}
   190  
   191  	hw := env.VirtualSystem.VirtualHardware[0]
   192  	if vmx := hw.System.VirtualSystemType; vmx != nil {
   193  		version := strings.Split(*vmx, ",")[0]
   194  		spec.ConfigSpec.Version = strings.TrimSpace(version)
   195  	}
   196  
   197  	ndisk := 0
   198  	ndev := 0
   199  	resources := make(map[string]types.BaseVirtualDevice)
   200  
   201  	for _, item := range hw.Item {
   202  		if cisp.DeploymentOption != "" && item.Configuration != nil {
   203  			if cisp.DeploymentOption != *item.Configuration {
   204  				continue
   205  			}
   206  		}
   207  
   208  		kind := func() string {
   209  			if item.ResourceSubType == nil {
   210  				return "unknown"
   211  			}
   212  			return strings.ToLower(*item.ResourceSubType)
   213  		}
   214  
   215  		unsupported := func(err error) {
   216  			result.Error = append(result.Error, types.LocalizedMethodFault{
   217  				Fault: &types.OvfUnsupportedType{
   218  					Name:       item.ElementName,
   219  					InstanceId: item.InstanceID,
   220  					DeviceType: int32(*item.ResourceType),
   221  				},
   222  				LocalizedMessage: err.Error(),
   223  			})
   224  		}
   225  
   226  		upload := func(file ovf.File, c types.BaseVirtualDevice, n int) {
   227  			result.FileItem = append(result.FileItem, types.OvfFileItem{
   228  				DeviceId: fmt.Sprintf("/%s/%s:%d", cisp.EntityName, device.Type(c), n),
   229  				Path:     file.Href,
   230  				Size:     int64(file.Size),
   231  				CimType:  int32(*item.ResourceType),
   232  			})
   233  		}
   234  
   235  		switch *item.ResourceType {
   236  		case 1: // VMCI
   237  		case 3: // Number of Virtual CPUs
   238  			spec.ConfigSpec.NumCPUs = int32(*item.VirtualQuantity)
   239  		case 4: // Memory Size
   240  			spec.ConfigSpec.MemoryMB = int64(*item.VirtualQuantity)
   241  		case 5: // IDE Controller
   242  			d, _ := device.CreateIDEController()
   243  			device = append(device, d)
   244  			resources[item.InstanceID] = d
   245  		case 6: // SCSI Controller
   246  			d, err := device.CreateSCSIController(kind())
   247  			if err == nil {
   248  				device = append(device, d)
   249  				resources[item.InstanceID] = d
   250  			} else {
   251  				unsupported(err)
   252  			}
   253  		case 10: // Virtual Network
   254  			net := ovfNetwork(ctx, req, item)
   255  			if net != nil {
   256  				d, err := device.CreateEthernetCard(kind(), net)
   257  				if err == nil {
   258  					device = append(device, d)
   259  				} else {
   260  					unsupported(err)
   261  				}
   262  			}
   263  		case 14: // Floppy Drive
   264  			if device.PickController((*types.VirtualSIOController)(nil)) == nil {
   265  				c := &types.VirtualSIOController{}
   266  				c.Key = device.NewKey()
   267  				device = append(device, c)
   268  			}
   269  			d, err := device.CreateFloppy()
   270  			if err == nil {
   271  				device = append(device, d)
   272  				resources[item.InstanceID] = d
   273  			} else {
   274  				unsupported(err)
   275  			}
   276  		case 15: // CD/DVD
   277  			c, ok := resources[*item.Parent]
   278  			if !ok {
   279  				continue // Parent is unsupported()
   280  			}
   281  			d, _ := device.CreateCdrom(c.(*types.VirtualIDEController))
   282  			if len(item.HostResource) != 0 {
   283  				for _, file := range env.References {
   284  					if strings.HasSuffix(item.HostResource[0], file.ID) {
   285  						path.Path = fmt.Sprintf("%s/_deviceImage%d.iso", cisp.EntityName, ndev)
   286  						device.InsertIso(d, path.String())
   287  						upload(file, d, ndev)
   288  						break
   289  					}
   290  				}
   291  			}
   292  			device = append(device, d)
   293  			ndev++
   294  		case 17: // Virtual Disk
   295  			c, ok := resources[*item.Parent]
   296  			if !ok {
   297  				continue // Parent is unsupported()
   298  			}
   299  			path.Path = fmt.Sprintf("%s/disk-%d.vmdk", cisp.EntityName, ndisk)
   300  			d := device.CreateDisk(c.(types.BaseVirtualController), ds.Reference(), path.String())
   301  
   302  			switch types.OvfCreateImportSpecParamsDiskProvisioningType(cisp.DiskProvisioning) {
   303  			case "",
   304  				types.OvfCreateImportSpecParamsDiskProvisioningTypeMonolithicFlat,
   305  				types.OvfCreateImportSpecParamsDiskProvisioningTypeFlat,
   306  				types.OvfCreateImportSpecParamsDiskProvisioningTypeEagerZeroedThick,
   307  				types.OvfCreateImportSpecParamsDiskProvisioningTypeThin,
   308  				types.OvfCreateImportSpecParamsDiskProvisioningTypeThick,
   309  				types.OvfCreateImportSpecParamsDiskProvisioningTypeSeSparse:
   310  				// OK
   311  			case types.OvfCreateImportSpecParamsDiskProvisioningTypeMonolithicSparse:
   312  				// results in DeviceUnsupportedForVmPlatform during import
   313  				d.VirtualDevice.Backing = new(types.VirtualDiskSparseVer2BackingInfo)
   314  			default:
   315  				result.Error = append(result.Error, types.LocalizedMethodFault{
   316  					Fault: &types.OvfUnsupportedDiskProvisioning{
   317  						DiskProvisioning: cisp.DiskProvisioning,
   318  					},
   319  					LocalizedMessage: "Disk provisioning type not supported: " + cisp.DiskProvisioning,
   320  				})
   321  			}
   322  
   323  			d.VirtualDevice.DeviceInfo = &types.Description{
   324  				Label: item.ElementName,
   325  			}
   326  			disk := ovfDisk(env, item.HostResource[0])
   327  			for _, file := range env.References {
   328  				if file.ID == *disk.FileRef {
   329  					upload(file, d, ndisk)
   330  					break
   331  				}
   332  			}
   333  			d.CapacityInKB = ovfDiskCapacity(disk)
   334  			device = append(device, d)
   335  			ndisk++
   336  		case 23: // USB Controller
   337  		case 24: // Video Card
   338  		default:
   339  			unsupported(fmt.Errorf("unsupported resource type: %d", *item.ResourceType))
   340  		}
   341  	}
   342  
   343  	spec.ConfigSpec.DeviceChange, _ = device.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
   344  
   345  	for _, p := range cisp.PropertyMapping {
   346  		spec.ConfigSpec.ExtraConfig = append(spec.ConfigSpec.ExtraConfig, &types.OptionValue{
   347  			Key:   p.Key,
   348  			Value: p.Value,
   349  		})
   350  	}
   351  
   352  	body.Res = &types.CreateImportSpecResponse{
   353  		Returnval: result,
   354  	}
   355  
   356  	return body
   357  }