diff --git a/govc/main.go b/govc/main.go index e73cf5a6e..474b5c4c6 100644 --- a/govc/main.go +++ b/govc/main.go @@ -67,6 +67,7 @@ import ( _ "github.com/vmware/govmomi/govc/vm/disk" _ "github.com/vmware/govmomi/govc/vm/guest" _ "github.com/vmware/govmomi/govc/vm/network" + _ "github.com/vmware/govmomi/govc/vm/rdm" _ "github.com/vmware/govmomi/govc/vm/snapshot" ) diff --git a/govc/vm/rdm/attach.go b/govc/vm/rdm/attach.go new file mode 100644 index 000000000..9c7ee2863 --- /dev/null +++ b/govc/vm/rdm/attach.go @@ -0,0 +1,149 @@ +/* +Copyright (c) 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 rdm + +import ( + "context" + "flag" + "fmt" + "strings" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/vim25/types" +) + +type attach struct { + *flags.VirtualMachineFlag + + device string +} + +func init() { + cli.Register("vm.rdm.attach", &attach{}) +} + +func (cmd *attach) Register(ctx context.Context, f *flag.FlagSet) { + + cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx) + cmd.VirtualMachineFlag.Register(ctx, f) + + f.StringVar(&cmd.device, "device", "", "Device Name") +} + +func (cmd *attach) Description() string { + return `Attach DEVICE to VM with RDM. + +Examples: + govc vm.rdm.attach -vm VM -device /vmfs/devices/disks/naa.000000000000000000000000000000000` +} + +func (cmd *attach) Process(ctx context.Context) error { + if err := cmd.VirtualMachineFlag.Process(ctx); err != nil { + return err + } + return nil +} + +//This piece of code was developped mainly thanks to the project govmax on github.com +//This file in particular https://github.com/codedellemc/govmax/blob/master/api/v1/vmomi.go +func (cmd *attach) Run(ctx context.Context, f *flag.FlagSet) error { + vm, err := cmd.VirtualMachine() + if err != nil { + return err + } + + if vm == nil { + return flag.ErrHelp + } + + devices, err := vm.Device(ctx) + if err != nil { + return err + } + + controller, err := devices.FindSCSIController("") + if err != nil { + return err + } + + vmConfigOptions, err := vm.QueryConfigTarget(ctx) + if err != nil { + return err + } + + for _, scsiDisk := range vmConfigOptions.ScsiDisk { + if !strings.Contains(scsiDisk.Disk.CanonicalName, cmd.device) { + continue + } + var backing types.VirtualDiskRawDiskMappingVer1BackingInfo + backing.CompatibilityMode = string(types.VirtualDiskCompatibilityModePhysicalMode) + backing.DeviceName = scsiDisk.Disk.DeviceName + for _, descriptor := range scsiDisk.Disk.Descriptor { + if strings.HasPrefix(descriptor.Id, "vml.") { + backing.LunUuid = descriptor.Id + break + } + } + var device types.VirtualDisk + device.Backing = &backing + device.ControllerKey = controller.VirtualController.Key + + var unitNumber *int32 + scsiCtrlUnitNumber := controller.VirtualController.UnitNumber + var u int32 + for u = 0; u < 16; u++ { + free := true + for _, d := range devices { + if d.GetVirtualDevice().ControllerKey == device.GetVirtualDevice().ControllerKey { + if u == *(d.GetVirtualDevice().UnitNumber) || u == *scsiCtrlUnitNumber { + free = false + } + } + } + if free { + unitNumber = &u + break + } + } + device.UnitNumber = unitNumber + + spec := types.VirtualMachineConfigSpec{} + + config := &types.VirtualDeviceConfigSpec{ + Device: &device, + Operation: types.VirtualDeviceConfigSpecOperationAdd, + } + + config.FileOperation = types.VirtualDeviceConfigSpecFileOperationCreate + + spec.DeviceChange = append(spec.DeviceChange, config) + + task, err := vm.Reconfigure(ctx, spec) + if err != nil { + return err + } + + err = task.Wait(ctx) + if err != nil { + return fmt.Errorf("Error adding device %+v \n with backing %+v \nLogged Item: %s", device, backing, err) + } + return nil + + } + return fmt.Errorf("Error: No LUN with device name containing %s found", cmd.device) +} diff --git a/govc/vm/rdm/ls.go b/govc/vm/rdm/ls.go new file mode 100644 index 000000000..1d5373f5d --- /dev/null +++ b/govc/vm/rdm/ls.go @@ -0,0 +1,109 @@ +/* +Copyright (c) 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 rdm + +import ( + "context" + "flag" + "fmt" + "io" + "os" + "strings" + "text/tabwriter" + + "github.com/vmware/govmomi/govc/cli" + "github.com/vmware/govmomi/govc/flags" + "github.com/vmware/govmomi/vim25/types" +) + +type ls struct { + *flags.VirtualMachineFlag + *flags.OutputFlag +} + +func init() { + cli.Register("vm.rdm.ls", &ls{}) +} + +func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) { + + cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx) + cmd.VirtualMachineFlag.Register(ctx, f) + cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) + cmd.OutputFlag.Register(ctx, f) +} + +func (cmd *ls) Description() string { + return `List available devices that could be attach to VM with RDM. + +Examples: + govc vm.rdm.ls -vm VM` +} + +func (cmd *ls) Process(ctx context.Context) error { + + if err := cmd.VirtualMachineFlag.Process(ctx); err != nil { + return err + } + if err := cmd.OutputFlag.Process(ctx); err != nil { + return err + } + return nil +} + +func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error { + vm, err := cmd.VirtualMachine() + if err != nil { + return err + } + + if vm == nil { + return flag.ErrHelp + } + + vmConfigOptions, err := vm.QueryConfigTarget(ctx) + if err != nil { + return err + } + + res := infoResult{ + Disks: vmConfigOptions.ScsiDisk, + } + return cmd.WriteResult(&res) +} + +type infoResult struct { + Disks []types.VirtualMachineScsiDiskDeviceInfo +} + +func (r *infoResult) Write(w io.Writer) error { + tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0) + for _, disk := range r.Disks { + fmt.Fprintf(tw, "Name:\t%s\n", disk.Name) + fmt.Fprintf(tw, " Device name:\t%s\n", disk.Disk.DeviceName) + fmt.Fprintf(tw, " Device path:\t%s\n", disk.Disk.DevicePath) + fmt.Fprintf(tw, " Canonical Name:\t%s\n", disk.Disk.CanonicalName) + + var uids []string + for _, descriptor := range disk.Disk.Descriptor { + uids = append(uids, descriptor.Id) + } + + fmt.Fprintf(tw, " UIDS:\t%s\n", strings.Join(uids, " ,")) + } + return tw.Flush() +} diff --git a/object/virtual_machine.go b/object/virtual_machine.go index 008d3e83b..87ae9a511 100644 --- a/object/virtual_machine.go +++ b/object/virtual_machine.go @@ -704,3 +704,24 @@ func (v VirtualMachine) Unregister(ctx context.Context) error { _, err := methods.UnregisterVM(ctx, v.Client(), &req) return err } + +// QueryEnvironmentBrowser is a helper to get the environmentBrowser property. +func (v VirtualMachine) QueryConfigTarget(ctx context.Context) (*types.ConfigTarget, error) { + var vm mo.VirtualMachine + + err := v.Properties(ctx, v.Reference(), []string{"environmentBrowser"}, &vm) + if err != nil { + return nil, err + } + + req := types.QueryConfigTarget{ + This: vm.EnvironmentBrowser, + } + + res, err := methods.QueryConfigTarget(ctx, v.Client(), &req) + if err != nil { + return nil, err + } + + return res.Returnval, nil +}