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