esxlib/service/service_vm.go

226 lines
5.3 KiB
Go

package service
import (
"context"
"fmt"
"strings"
"esxlib/api/cloud"
"esxlib/esx"
"git.twelvetwelve.org/library/core/log"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func splitVmID(id string) (string, string) {
vals := strings.SplitN(id, ":", 2)
if len(vals) == 1 {
return "", ""
}
// zone, id
return vals[0], vals[1]
}
func vmToInfo(vm *esx.VirtualMachine, zoneId string) *cloud.VMInfo {
info := &cloud.VMInfo{
Id: fmt.Sprintf("%s:%s", zoneId, vm.Id()),
Name: vm.Name(),
Zone: zoneId,
}
switch vm.State() {
case esx.PowerStateOff:
info.State = cloud.PowerState_OFF
case esx.PowerStateOn:
info.State = cloud.PowerState_ON
case esx.PowerStateSuspended:
info.State = cloud.PowerState_SUSPENDED
case esx.PowerStateUndefined:
info.State = cloud.PowerState_UNKNOWN
}
for _, vmNet := range vm.GetNetworkAddressV4() {
info.Network = append(info.Network, &cloud.VMNetworkInfo{
Address: vmNet,
})
}
return info
}
func (cs *CloudServer) VMList(ctx context.Context, req *cloud.VMListRequest) (*cloud.VMListResponse, error) {
resp := &cloud.VMListResponse{}
for zoneId := range cs.esx {
fmt.Printf("Loading zone: %s\n", zoneId)
zone := cs.esx[zoneId]
ls, err := zone.VirtualMachines.List(ctx)
if err != nil {
log.Errorf("VM from zone: %s failed err:%s\n", zoneId, err.Error())
return nil, err
}
for idx := range ls {
resp.Vms = append(resp.Vms, vmToInfo(ls[idx], zoneId))
}
}
return resp, nil
}
func (cs *CloudServer) VMGet(ctx context.Context, req *cloud.VMGetRequest) (*cloud.VMGetResponse, error) {
switch search := req.Search.(type) {
case *cloud.VMGetRequest_Id:
zoneId, vmId := splitVmID(search.Id)
zone, ok := cs.esx[zoneId]
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "invalid vm id")
}
vm, err := zone.VirtualMachines.GetById(ctx, vmId)
if err != nil {
return nil, err
}
resp := &cloud.VMGetResponse{
Vm: vmToInfo(vm, zoneId),
}
return resp, err
}
return nil, status.Errorf(codes.Unimplemented, "method VMGet not implemented")
}
func (cs *CloudServer) VMPower(ctx context.Context, req *cloud.VMPowerRequest) (*cloud.VMPowerResponse, error) {
zoneId, vmId := splitVmID(req.Id)
zone, ok := cs.esx[zoneId]
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "invalid vm id")
}
vm, err := zone.VirtualMachines.GetById(ctx, vmId)
if err != nil {
return nil, err
}
switch req.State {
case cloud.PowerState_OFF:
err = vm.PowerOff(ctx)
case cloud.PowerState_ON:
err = vm.PowerOn(ctx)
case cloud.PowerState_SUSPENDED:
err = vm.Suspend(ctx)
default:
return nil, fmt.Errorf("unsupported")
}
if err != nil {
return nil, err
}
return &cloud.VMPowerResponse{}, nil
}
func (cs *CloudServer) VMCreate(ctx context.Context, req *cloud.VMCreateRequest) (*cloud.VMCreateResponse, error) {
zone, ok := cs.esx[req.ZoneId]
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "invalid vm id")
}
var hosts string
for idx := range req.AuthorizedHosts {
hosts = hosts + req.AuthorizedHosts[idx] + "\n"
}
vm, err := zone.VirtualMachines.Create(ctx, req.Slug, req.Name, hosts, []byte(req.InitScript))
if err != nil {
return nil, err
}
err = vm.PowerOn(ctx)
if err != nil {
log.Warnf("VM: %s:%s (%s) created but not powered on.", req.ZoneId, vm.Id(), vm.Name())
}
_ = vm.Update()
return &cloud.VMCreateResponse{
Vm: vmToInfo(vm, req.ZoneId),
}, nil
}
func (cs *CloudServer) VMDestroy(ctx context.Context, req *cloud.VMDestroyRequest) (*cloud.VMDestroyResponse, error) {
zoneId, vmId := splitVmID(req.Id)
zone, ok := cs.esx[zoneId]
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "invalid vm id")
}
log.Eventf("VM Destroy: %s\n", req.Id)
err := zone.VirtualMachines.Destroy(ctx, vmId)
if err != nil {
return nil, err
}
log.Eventf("VM Destroy: %s OK\n", req.Id)
return &cloud.VMDestroyResponse{}, nil
}
func (cs *CloudServer) VMListSlugs(ctx context.Context, req *cloud.VMListSlugsRequest) (*cloud.VMListSlugsResponse, error) {
zone, ok := cs.config.Zones[req.ZoneId]
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "invalid zone id")
}
resp := &cloud.VMListSlugsResponse{}
for slugId, slug := range zone.HostDefaults.Slugs {
resp.Slugs = append(resp.Slugs, &cloud.Slug{
Id: slugId,
Name: slug.Image,
Description: slug.Description,
CpuCount: uint32(slug.CpuCount),
MemoryMb: uint32(slug.MemoryMB),
DiskGb: uint32(slug.DiskSizeGB),
Cost: 0,
})
}
return resp, nil
}
func (cs *CloudServer) ZonesList(ctx context.Context, req *cloud.ZonesListRequest) (*cloud.ZonesListResponse, error) {
resp := &cloud.ZonesListResponse{}
for zoneId, zone := range cs.config.Zones {
resp.Zones = append(resp.Zones, &cloud.Zone{
Id: zoneId,
Name: zone.Name,
Description: zone.Description,
})
}
return resp, nil
}
func (cs *CloudServer) NetworksList(ctx context.Context, req *cloud.NetworksListRequest) (*cloud.NetworksListResponse, error) {
zone, ok := cs.esx[req.ZoneId]
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "invalid zone id")
}
networks, err := zone.Networks.List(ctx)
if err != nil {
return nil, err
}
resp := &cloud.NetworksListResponse{}
for _, net := range networks {
resp.Networks = append(resp.Networks, &cloud.Network{
Id: net.Id,
Name: net.Name,
VLAN: net.VLAN,
})
}
return resp, err
}