226 lines
5.3 KiB
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
|
|
}
|