Merge pull request #1929 from thebsdbox/push_vcenter

vCenter push capability
This commit is contained in:
Riyaz Faizullabhoy 2017-05-31 13:41:24 -07:00 committed by GitHub
commit abb19f847d
3 changed files with 150 additions and 65 deletions

View File

@ -15,6 +15,7 @@ func pushUsage() {
fmt.Printf("'backend' specifies the push backend.\n") fmt.Printf("'backend' specifies the push backend.\n")
fmt.Printf("Supported backends are\n") fmt.Printf("Supported backends are\n")
fmt.Printf(" gcp\n") fmt.Printf(" gcp\n")
fmt.Printf(" vcenter\n")
fmt.Printf("\n") fmt.Printf("\n")
fmt.Printf("'options' are the backend specific options.\n") fmt.Printf("'options' are the backend specific options.\n")
fmt.Printf("See '%s push [backend] --help' for details.\n\n", invoked) fmt.Printf("See '%s push [backend] --help' for details.\n\n", invoked)
@ -33,6 +34,8 @@ func push(args []string) {
os.Exit(0) os.Exit(0)
case "gcp": case "gcp":
pushGcp(args[1:]) pushGcp(args[1:])
case "vcenter":
pushVCenter(args[1:])
default: default:
log.Errorf("No 'push' backend specified.") log.Errorf("No 'push' backend specified.")
} }

View File

@ -0,0 +1,70 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"path"
"path/filepath"
"strings"
log "github.com/Sirupsen/logrus"
)
// Process the push arguments and execute push
func pushVCenter(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 of VMware vCenter in the format of https://username:password@VCaddress/sdk")
newVM.dsName = flags.String("datastore", os.Getenv("VCDATASTORE"), "The name of the DataStore to host the image")
newVM.networkName = flags.String("network", os.Getenv("VCNETWORK"), "The network label the VM will use")
newVM.vSphereHost = flags.String("hostname", os.Getenv("VCHOST"), "The server that will host the image")
newVM.path = flags.String("path", "", "Path to a specific image")
newVM.vmFolder = flags.String("folder", "", "A folder on the datastore to push the image too")
flags.Usage = func() {
fmt.Printf("USAGE: %s push vcenter [options] path \n\n", invoked)
fmt.Printf("'path' specifies the full path of an image that will be pushed\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 path to the image to push\n")
flags.Usage()
os.Exit(1)
}
*newVM.path = remArgs[0]
// Ensure an iso has been passed to the vCenter push Command
if !strings.HasSuffix(*newVM.path, ".iso") {
log.Fatalln("Please pass an \".iso\" file as the path")
}
// Test any passed in files before uploading image
checkFile(*newVM.path)
// Connect to VMware vCenter and return the values needed to upload image
c, dss, _, _, _, _ := vCenterConnect(ctx, newVM)
// Create a folder from the uploaded image name if needed
if *newVM.vmFolder == "" {
*newVM.vmFolder = strings.TrimSuffix(path.Base(*newVM.path), ".iso")
}
// The CreateFolder method isn't necessary as the *newVM.vmname will be created automatically
uploadFile(c, newVM, dss)
}

View File

@ -25,7 +25,7 @@ type vmConfig struct {
networkName *string networkName *string
vSphereHost *string vSphereHost *string
vmName *string vmFolder *string
path *string path *string
persistent *string persistent *string
persistentSz int persistentSz int
@ -43,12 +43,12 @@ func runVcenter(args []string) {
flags := flag.NewFlagSet("vCenter", flag.ExitOnError) flags := flag.NewFlagSet("vCenter", flag.ExitOnError)
invoked := filepath.Base(os.Args[0]) 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.vCenterURL = flags.String("url", os.Getenv("VCURL"), "URL of VMware vCenter in the format of https://username:password@VCaddress/sdk")
newVM.dsName = flags.String("datastore", os.Getenv("VMDATASTORE"), "The Name of the DataStore to host the VM") newVM.dsName = flags.String("datastore", os.Getenv("VCDATASTORE"), "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.networkName = flags.String("network", os.Getenv("VCNETWORK"), "The network label the VM will use")
newVM.vSphereHost = flags.String("hostname", os.Getenv("VMHOST"), "The Server that will run the VM") newVM.vSphereHost = flags.String("hostname", os.Getenv("VCHOST"), "The server that will run the VM")
newVM.vmName = flags.String("vmname", "", "Specify a name for virtual Machine") newVM.vmFolder = flags.String("vmfolder", "", "Specify a name/folder for the virtual machine to reside in")
newVM.path = flags.String("path", "", "Path to a specific image") newVM.path = flags.String("path", "", "Path to a specific image")
newVM.persistent = flags.String("persistentSize", "", "Size in MB of persistent storage to allocate to the VM") newVM.persistent = flags.String("persistentSize", "", "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.mem = flags.Int64("mem", 1024, "Size in MB of memory to allocate to the VM")
@ -56,8 +56,8 @@ func runVcenter(args []string) {
newVM.poweron = flags.Bool("powerOn", false, "Power On the new VM once it has been created") newVM.poweron = flags.Bool("powerOn", false, "Power On the new VM once it has been created")
flags.Usage = func() { flags.Usage = func() {
fmt.Printf("USAGE: %s run vcenter [options] [name]\n\n", invoked) fmt.Printf("USAGE: %s run vcenter [options] path\n\n", invoked)
fmt.Printf("'name' specifies the full path of an image that will be ran\n") fmt.Printf("'path' specifies the full path of an image to run\n")
fmt.Printf("Options:\n\n") fmt.Printf("Options:\n\n")
flags.PrintDefaults() flags.PrintDefaults()
} }
@ -77,65 +77,22 @@ func runVcenter(args []string) {
// Ensure an iso has been passed to the vCenter run Command // Ensure an iso has been passed to the vCenter run Command
if strings.HasSuffix(*newVM.path, ".iso") { if strings.HasSuffix(*newVM.path, ".iso") {
// Allow alternative names for new virtual machines being created in vCenter // Allow alternative names for new virtual machines being created in vCenter
if *newVM.vmName == "" { if *newVM.vmFolder == "" {
*newVM.vmName = strings.TrimSuffix(path.Base(*newVM.path), ".iso") *newVM.vmFolder = strings.TrimSuffix(path.Base(*newVM.path), ".iso")
} }
} else { } else {
log.Fatalln("Ensure that an \".iso\" file is used as part of the path") log.Fatalln("Please pass an \".iso\" file as the path")
} }
// Test any passed in files before creating a new VM // Test any passed in files before creating a new VM
checkFile(*newVM.path) checkFile(*newVM.path)
// Parse URL from string // Connect to VMware vCenter and return the default and found values needed for a new VM
u, err := url.Parse(*newVM.vCenterURL) c, dss, folders, hs, net, rp := vCenterConnect(ctx, newVM)
if err != nil {
log.Fatalf("URL can't be parsed, ensure it is https://username:password/<address>/sdk")
}
// 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") log.Infof("Creating new LinuxKit Virtual Machine")
spec := types.VirtualMachineConfigSpec{ spec := types.VirtualMachineConfigSpec{
Name: *newVM.vmName, Name: *newVM.vmFolder,
GuestId: "otherLinux64Guest", GuestId: "otherLinux64Guest",
Files: &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", dss.Name())}, Files: &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", dss.Name())},
NumCPUs: int32(*newVM.vCpus), NumCPUs: int32(*newVM.vCpus),
@ -152,11 +109,6 @@ func runVcenter(args []string) {
Device: scsi, 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) task, err := folders.VmFolder.CreateVM(ctx, spec, rp, hs)
if err != nil { if err != nil {
log.Fatalln("Creating new VM failed, more detail can be found in vCenter tasks") log.Fatalln("Creating new VM failed, more detail can be found in vCenter tasks")
@ -191,6 +143,66 @@ func runVcenter(args []string) {
} }
} }
func vCenterConnect(ctx context.Context, newVM vmConfig) (*govmomi.Client, *object.Datastore, *object.DatacenterFolders, *object.HostSystem, object.NetworkReference, *object.ResourcePool) {
// 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 %v", err)
}
// Connect and log in to ESX or vCenter
c, err := govmomi.NewClient(ctx, u, true)
if err != nil {
log.Fatalf("Error logging into vCenter, check address and credentials %v", err)
}
// Create a new finder that will discover the defaults and are looked for Networks/Datastores
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.Fatalf("No Datacenter instance could be found inside of vCenter %v", err)
}
// 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)
}
folders, err := dc.Folders(ctx)
if err != nil {
log.Fatalln("Error locating default datacenter folder")
}
// Check if network connectivity is requested
var net object.NetworkReference
if *newVM.networkName != "" {
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")
}
return c, dss, folders, hs, net, rp
}
func powerOnVM(ctx context.Context, vm *object.VirtualMachine) { func powerOnVM(ctx context.Context, vm *object.VirtualMachine) {
task, err := vm.PowerOn(ctx) task, err := vm.PowerOn(ctx)
if err != nil { if err != nil {
@ -209,7 +221,7 @@ func uploadFile(c *govmomi.Client, newVM vmConfig, dss *object.Datastore) {
if *newVM.path == "" { if *newVM.path == "" {
log.Fatalf("No file specified") log.Fatalf("No file specified")
} }
dsurl := dss.NewURL(fmt.Sprintf("%s/%s", *newVM.vmName, fileName)) dsurl := dss.NewURL(fmt.Sprintf("%s/%s", *newVM.vmFolder, fileName))
p := soap.DefaultUpload p := soap.DefaultUpload
if err := c.Client.UploadFile(*newVM.path, dsurl, &p); err != nil { if err := c.Client.UploadFile(*newVM.path, dsurl, &p); err != nil {
@ -248,7 +260,7 @@ func addVMDK(ctx context.Context, vm *object.VirtualMachine, dss *object.Datasto
log.Fatalf("Unable to find SCSI device from VM configuration\n%v", err) log.Fatalf("Unable to find SCSI device from VM configuration\n%v", err)
} }
// The default is to have all persistent disks named linuxkit.vmdk // 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 := devices.CreateDisk(controller, dss.Reference(), dss.Path(fmt.Sprintf("%s/%s", *newVM.vmFolder, "linuxkit.vmdk")))
disk.CapacityInKB = int64(newVM.persistentSz * 1024) disk.CapacityInKB = int64(newVM.persistentSz * 1024)
@ -279,7 +291,7 @@ func addISO(ctx context.Context, newVM vmConfig, vm *object.VirtualMachine, dss
} }
var add []types.BaseVirtualDevice var add []types.BaseVirtualDevice
add = append(add, devices.InsertIso(cdrom, dss.Path(fmt.Sprintf("%s/%s", *newVM.vmName, path.Base(*newVM.path))))) add = append(add, devices.InsertIso(cdrom, dss.Path(fmt.Sprintf("%s/%s", *newVM.vmFolder, path.Base(*newVM.path)))))
log.Infof("Adding ISO to the Virtual Machine") log.Infof("Adding ISO to the Virtual Machine")