Skip to content

Commit

Permalink
Merge pull request #1929 from thebsdbox/push_vcenter
Browse files Browse the repository at this point in the history
vCenter push capability
  • Loading branch information
riyazdf committed May 31, 2017
2 parents b5b0c08 + 6d9a3c5 commit abb19f8
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 65 deletions.
3 changes: 3 additions & 0 deletions src/cmd/linuxkit/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func pushUsage() {
fmt.Printf("'backend' specifies the push backend.\n")
fmt.Printf("Supported backends are\n")
fmt.Printf(" gcp\n")
fmt.Printf(" vcenter\n")
fmt.Printf("\n")
fmt.Printf("'options' are the backend specific options.\n")
fmt.Printf("See '%s push [backend] --help' for details.\n\n", invoked)
Expand All @@ -33,6 +34,8 @@ func push(args []string) {
os.Exit(0)
case "gcp":
pushGcp(args[1:])
case "vcenter":
pushVCenter(args[1:])
default:
log.Errorf("No 'push' backend specified.")
}
Expand Down
70 changes: 70 additions & 0 deletions src/cmd/linuxkit/push_vcenter.go
Original file line number Diff line number Diff line change
@@ -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)
}
142 changes: 77 additions & 65 deletions src/cmd/linuxkit/run_vcenter.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type vmConfig struct {
networkName *string
vSphereHost *string

vmName *string
vmFolder *string
path *string
persistent *string
persistentSz int
Expand All @@ -43,21 +43,21 @@ func runVcenter(args []string) {
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.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 VM")
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 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.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.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 run vcenter [options] [name]\n\n", invoked)
fmt.Printf("'name' specifies the full path of an image that will be ran\n")
fmt.Printf("USAGE: %s run vcenter [options] path\n\n", invoked)
fmt.Printf("'path' specifies the full path of an image to run\n")
fmt.Printf("Options:\n\n")
flags.PrintDefaults()
}
Expand All @@ -77,65 +77,22 @@ func runVcenter(args []string) {
// Ensure an iso has been passed to the vCenter run Command
if strings.HasSuffix(*newVM.path, ".iso") {
// Allow alternative names for new virtual machines being created in vCenter
if *newVM.vmName == "" {
*newVM.vmName = strings.TrimSuffix(path.Base(*newVM.path), ".iso")
if *newVM.vmFolder == "" {
*newVM.vmFolder = strings.TrimSuffix(path.Base(*newVM.path), ".iso")
}
} 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
checkFile(*newVM.path)

// 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")
}

// 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")
}
// Connect to VMware vCenter and return the default and found values needed for a new VM
c, dss, folders, hs, net, rp := vCenterConnect(ctx, newVM)

log.Infof("Creating new LinuxKit Virtual Machine")
spec := types.VirtualMachineConfigSpec{
Name: *newVM.vmName,
Name: *newVM.vmFolder,
GuestId: "otherLinux64Guest",
Files: &types.VirtualMachineFileInfo{VmPathName: fmt.Sprintf("[%s]", dss.Name())},
NumCPUs: int32(*newVM.vCpus),
Expand All @@ -152,11 +109,6 @@ func runVcenter(args []string) {
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")
Expand Down Expand Up @@ -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) {
task, err := vm.PowerOn(ctx)
if err != nil {
Expand All @@ -209,7 +221,7 @@ func uploadFile(c *govmomi.Client, newVM vmConfig, dss *object.Datastore) {
if *newVM.path == "" {
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
if err := c.Client.UploadFile(*newVM.path, dsurl, &p); err != nil {
Expand Down Expand Up @@ -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)
}
// 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)

Expand Down Expand Up @@ -279,7 +291,7 @@ func addISO(ctx context.Context, newVM vmConfig, vm *object.VirtualMachine, dss
}

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")

Expand Down

0 comments on commit abb19f8

Please sign in to comment.