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 }