VMware vCenter run capability

Signed-off-by: Dan Finneran <daniel.finneran@gmail.com>
This commit is contained in:
thebsdbox 2017-05-23 14:39:30 +01:00
parent 5500302ba5
commit 1e6243357f
3 changed files with 296 additions and 0 deletions

View File

@ -19,6 +19,7 @@ func runUsage() {
fmt.Printf(" gcp\n")
fmt.Printf(" hyperkit [macOS]\n")
fmt.Printf(" qemu [linux]\n")
fmt.Printf(" vcenter\n")
fmt.Printf(" vmware\n")
fmt.Printf(" packet\n")
fmt.Printf("\n")
@ -48,6 +49,8 @@ func run(args []string) {
runQemu(args[1:])
case "packet":
runPacket(args[1:])
case "vcenter":
runVcenter(args[1:])
default:
switch runtime.GOOS {
case "darwin":

View File

@ -0,0 +1,292 @@
package main
import (
"context"
"flag"
"fmt"
"net/url"
"os"
"path"
"path/filepath"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
log "github.com/Sirupsen/logrus"
)
type vmConfig struct {
vCenterURL *string
dsName *string
networkName *string
vSphereHost *string
vmName *string
path *string
persistent *int64
vCpus *int
mem *int64
poweron *bool
}
func runVcenter(args []string) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var newVM vmConfig
flags := flag.NewFlagSet("vCenter", flag.ExitOnError)
invoked := filepath.Base(os.Args[0])
newVM.vCenterURL = flags.String("url", os.Getenv("VCURL"), "URL in the format of https://username:password@host/sdk")
newVM.dsName = flags.String("datastore", os.Getenv("VMDATASTORE"), "The Name of the DataStore to host the VM")
newVM.networkName = flags.String("network", os.Getenv("VMNETWORK"), "The VMware vSwitch the VM will use")
newVM.vSphereHost = flags.String("hostname", os.Getenv("VMHOST"), "The Server that will run the VM")
newVM.vmName = flags.String("vmname", "", "Specify a name for virtual Machine")
newVM.path = flags.String("path", "", "Path to a specific image")
newVM.persistent = flags.Int64("persistentSize", 0, "Size in MB of persistent storage to allocate to the VM")
newVM.mem = flags.Int64("mem", 1024, "Size in MB of memory to allocate to the VM")
newVM.vCpus = flags.Int("cpus", 1, "Amount of vCPUs to allocate to the VM")
newVM.poweron = flags.Bool("powerOn", false, "Power On the new VM once it has been created")
flags.Usage = func() {
fmt.Printf("USAGE: %s push vcenter [options] [name]\n\n", invoked)
fmt.Printf("'name' specifies the full path of an image file which will be uploaded\n")
fmt.Printf("Options:\n\n")
flags.PrintDefaults()
}
if err := flags.Parse(args); err != nil {
log.Fatalln("Unable to parse args")
}
remArgs := flags.Args()
if len(remArgs) == 0 {
fmt.Printf("Please specify the prefix to the image to push\n")
flags.Usage()
os.Exit(1)
}
prefix := remArgs[0]
// Allow alternative names for new virtual machines being created in vCenter
if *newVM.vmName == "" {
*newVM.vmName = prefix
}
// Parse URL from string
u, err := url.Parse(*newVM.vCenterURL)
if err != nil {
log.Fatalf("URL can't be parsed, ensure it is https://username:password/<address>/sdk")
}
// Test any passed in files before creating a new VM
if *newVM.path == "" {
*newVM.path = prefix + ".iso"
}
checkFile(*newVM.path)
// Connect and log in to ESX or vCenter
c, err := govmomi.NewClient(ctx, u, true)
if err != nil {
log.Fatalln("Error logging into vCenter, check address and credentials")
}
f := find.NewFinder(c.Client, true)
// Find one and only datacenter, not sure how VMware linked mode will work
dc, err := f.DefaultDatacenter(ctx)
if err != nil {
log.Fatalln("No Datacenter instance could be found inside of vCenter")
}
// Make future calls local to this datacenter
f.SetDatacenter(dc)
// Find Datastore/Network
dss, err := f.DatastoreOrDefault(ctx, *newVM.dsName)
if err != nil {
log.Fatalf("Datastore [%s], could not be found", *newVM.dsName)
}
net, err := f.NetworkOrDefault(ctx, *newVM.networkName)
if err != nil {
log.Fatalf("Network [%s], could not be found", *newVM.networkName)
}
// Set the host that the VM will be created on
hs, err := f.HostSystemOrDefault(ctx, *newVM.vSphereHost)
if err != nil {
log.Fatalf("vSphere host [%s], could not be found", *newVM.vSphereHost)
}
var rp *object.ResourcePool
rp, err = hs.ResourcePool(ctx)
if err != nil {
log.Fatalln("Error locating default resource pool")
}
log.Infof("Creating new LinuxKit Virtual Machine")
spec := types.VirtualMachineConfigSpec{
Name: *newVM.vmName,
GuestId: "otherLinux64Guest",
Files: &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", dss.Name())},
NumCPUs: int32(*newVM.vCpus),
MemoryMB: *newVM.mem,
}
scsi, err := object.SCSIControllerTypes().CreateSCSIController("pvscsi")
if err != nil {
log.Fatalln("Error creating pvscsi controller as part of new VM")
}
spec.DeviceChange = append(spec.DeviceChange, &types.VirtualDeviceConfigSpec{
Operation: types.VirtualDeviceConfigSpecOperationAdd,
Device: scsi,
})
folders, err := dc.Folders(ctx)
if err != nil {
log.Fatalln("Error locating default datacenter folder")
}
task, err := folders.VmFolder.CreateVM(ctx, spec, rp, hs)
if err != nil {
log.Fatalln("Creating new VM failed, more detail can be found in vCenter tasks")
}
info, err := task.WaitForResult(ctx, nil)
if err != nil {
log.Fatalln("Creating new VM failed, more detail can be found in vCenter tasks")
}
// Retrieve the new VM
vm := object.NewVirtualMachine(c.Client, info.Result.(types.ManagedObjectReference))
uploadFile(c, newVM, dss)
addISO(ctx, newVM, vm, dss)
if *newVM.persistent != 0 {
addVMDK(ctx, vm, dss, newVM)
}
if *newVM.networkName != "" {
addNIC(ctx, vm, net)
}
if *newVM.poweron == true {
log.Infoln("Powering on LinuxKit VM")
powerOnVM(ctx, vm)
}
}
func powerOnVM(ctx context.Context, vm *object.VirtualMachine) {
task, err := vm.PowerOn(ctx)
if err != nil {
log.Errorln("Power On operation has failed, more detail can be found in vCenter tasks")
}
_, err = task.WaitForResult(ctx, nil)
if err != nil {
log.Errorln("Power On Task has failed, more detail can be found in vCenter tasks")
}
}
func uploadFile(c *govmomi.Client, newVM vmConfig, dss *object.Datastore) {
_, fileName := path.Split(*newVM.path)
log.Infof("Uploading LinuxKit file [%s]", *newVM.path)
if *newVM.path == "" {
log.Fatalf("No file specified")
}
dsurl := dss.NewURL(fmt.Sprintf("%s/%s", *newVM.vmName, fileName))
p := soap.DefaultUpload
if err := c.Client.UploadFile(*newVM.path, dsurl, &p); err != nil {
log.Fatalf("Unable to upload file to vCenter Datastore\n%v", err)
}
}
func addNIC(ctx context.Context, vm *object.VirtualMachine, net object.NetworkReference) {
backing, err := net.EthernetCardBackingInfo(ctx)
if err != nil {
log.Fatalf("Unable to determine vCenter network backend\n%v", err)
}
netdev, err := object.EthernetCardTypes().CreateEthernetCard("vmxnet3", backing)
if err != nil {
log.Fatalf("Unable to create vmxnet3 network interface\n%v", err)
}
log.Infof("Adding VM Networking")
var add []types.BaseVirtualDevice
add = append(add, netdev)
if vm.AddDevice(ctx, add...); err != nil {
log.Fatalf("Unable to add new networking device to VM configuration\n%v", err)
}
}
func addVMDK(ctx context.Context, vm *object.VirtualMachine, dss *object.Datastore, newVM vmConfig) {
devices, err := vm.Device(ctx)
if err != nil {
log.Fatalf("Unable to read devices from VM configuration\n%v", err)
}
controller, err := devices.FindDiskController("scsi")
if err != nil {
log.Fatalf("Unable to find SCSI device from VM configuration\n%v", err)
}
// The default is to have all persistent disks named linuxkit.vmdk
disk := devices.CreateDisk(controller, dss.Reference(), dss.Path(fmt.Sprintf("%s/%s", *newVM.vmName, "linuxkit.vmdk")))
disk.CapacityInKB = *newVM.persistent * 1024
var add []types.BaseVirtualDevice
add = append(add, disk)
log.Infof("Adding a persistent disk to the Virtual Machine")
if vm.AddDevice(ctx, add...); err != nil {
log.Fatalf("Unable to add new storage device to VM configuration\n%v", err)
}
}
func addISO(ctx context.Context, newVM vmConfig, vm *object.VirtualMachine, dss *object.Datastore) {
devices, err := vm.Device(ctx)
if err != nil {
log.Fatalf("Unable to read devices from VM configuration\n%v", err)
}
ide, err := devices.FindIDEController("")
if err != nil {
log.Fatalf("Unable to find IDE device from VM configuration\n%v", err)
}
cdrom, err := devices.CreateCdrom(ide)
if err != nil {
log.Fatalf("Unable to create new CDROM device\n%v", err)
}
var add []types.BaseVirtualDevice
add = append(add, devices.InsertIso(cdrom, dss.Path(fmt.Sprintf("%s/%s", *newVM.vmName, "linuxkit.iso"))))
log.Infof("Adding ISO to the Virtual Machine")
if vm.AddDevice(ctx, add...); err != nil {
log.Fatalf("Unable to add new CD-ROM device to VM configuration\n%v", err)
}
}
func checkFile(file string) {
if _, err := os.Stat(file); err != nil {
if os.IsPermission(err) {
log.Fatalf("Unable to read file [%s], please check permissions", file)
}
if os.IsNotExist(err) {
log.Fatalf("File [%s], does not exist", file)
}
}
}

View File

@ -10,6 +10,7 @@ github.com/packethost/packngo 91d54000aa56874149d348a884ba083c41d38091
github.com/rneugeba/iso9660wrap 4606f848a055435cdef85305960b0e1bb788d506
github.com/satori/go.uuid b061729afc07e77a8aa4fad0a2fd840958f1942a
github.com/surma/gocpio fcb68777e7dc4ea43ffce871b552c0d073c17495
github.com/vmware/govmomi 6f8ebd89d521d9f9af7a6c2219c4deee511020dd
golang.org/x/crypto 573951cbe80bb6352881271bb276f48749eab6f4
golang.org/x/net a6577fac2d73be281a500b310739095313165611
golang.org/x/oauth2 1611bb46e67abc64a71ecc5c3ae67f1cbbc2b921