1// Copyright 2017 VMware, Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package disk 16 17import ( 18 "net/url" 19 20 "github.com/vmware/govmomi/task" 21 "github.com/vmware/govmomi/vim25/mo" 22 "github.com/vmware/govmomi/vim25/soap" 23 "github.com/vmware/govmomi/vim25/types" 24 "github.com/vmware/vic/pkg/errors" 25 "github.com/vmware/vic/pkg/trace" 26 "github.com/vmware/vic/pkg/vsphere/datastore" 27 "github.com/vmware/vic/pkg/vsphere/session" 28) 29 30// Vmdk is intended to be embedded by stores that manage VMDK-based data resources 31type Vmdk struct { 32 *Manager 33 *datastore.Helper 34 *session.Session 35} 36 37const ( 38 DiskBackendKey = "msg.disk.noBackEnd" 39 LockedFileKey = "msg.fileio.lock" 40) 41 42// Mount mounts the disk, returning the mount path and the function used to unmount/detaches 43// when no longer in use 44func (v *Vmdk) Mount(op trace.Operation, uri *url.URL, persistent bool) (string, func(), error) { 45 if uri.Scheme != "ds" { 46 return "", nil, errors.New("vmdk path must be a datastore url with \"ds\" scheme") 47 } 48 49 dsPath, err := datastore.PathFromString(uri.Path) 50 if err != nil { 51 return "", nil, err 52 } 53 54 cleanFunc := func() { 55 if err := v.UnmountAndDetach(op, dsPath, persistent); err != nil { 56 op.Errorf("Error cleaning up disk: %s", err.Error()) 57 } 58 } 59 60 mountPath, err := v.AttachAndMount(op, dsPath, persistent) 61 return mountPath, cleanFunc, err 62} 63 64func LockedVMDKFilter(vm *mo.VirtualMachine) bool { 65 if vm == nil { 66 return false 67 } 68 69 return vm.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOn 70} 71 72// IsLockedError will determine if the error received is: 73// a. related to a vmdk 74// b. due to the vmdk being locked 75// It will return false in absence of confirmation, meaning incomplete vim errors 76// will return false 77func IsLockedError(err error) bool { 78 disks := LockedDisks(err) 79 //if device is locked, disks will not be nil 80 return len(disks) > 0 81} 82 83// LockedDisks returns locked devices path in the error if it's device lock error 84func LockedDisks(err error) []string { 85 var faultMessage []types.LocalizableMessage 86 87 if soap.IsSoapFault(err) { 88 switch f := soap.ToSoapFault(err).VimFault().(type) { 89 case *types.GenericVmConfigFault: 90 faultMessage = f.FaultMessage 91 } 92 } else if soap.IsVimFault(err) { 93 faultMessage = soap.ToVimFault(err).GetMethodFault().FaultMessage 94 } else { 95 switch err := err.(type) { 96 case task.Error: 97 faultMessage = err.Fault().GetMethodFault().FaultMessage 98 } 99 } 100 101 if faultMessage == nil { 102 return nil 103 } 104 105 lockedFile := false 106 var devices []string 107 for _, message := range faultMessage { 108 switch message.Key { 109 case LockedFileKey: 110 lockedFile = true 111 case DiskBackendKey: 112 for _, arg := range message.Arg { 113 if device, ok := arg.Value.(string); ok { 114 devices = append(devices, device) 115 continue 116 } 117 } 118 } 119 } 120 if lockedFile { 121 // make sure locked devices are returned only when both keys appear in the error 122 return devices 123 } 124 return nil 125} 126