diff --git a/Makefile b/Makefile index 5485dab..22e9b3a 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ GO_LDFLAGS +=" TAG ?= $(COMMIT) -GOLANGCI_LINTER_VERSION = v1.9.3 +GOLANGCI_LINTER_VERSION = v1.10 all: lint test docker diff --git a/README.md b/README.md index ef8de56..48847ad 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ See available environment variables examples in [.env.example](https://github.co ```bash export VMWARE_URL=username:password@vsphere.address.com export VMWARE_INSECURE=1 -export VMWARE_DC=DC1 +export VMWARE_DATACENTER=DC1 +export VMWARE_FOLDER=DevVMs ``` ## Development @@ -30,5 +31,5 @@ or using Docker and Docker Compose make dc ``` -## API +## API docs See [swagger file](https://github.com/vterdunov/janna-api/blob/master/api/swagger.yaml) diff --git a/api/swagger.yaml b/api/swagger.yaml index fb3a276..69f780a 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -65,6 +65,11 @@ paths: tags: - Virtual Machines parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: datacenter in: query description: Datacenter name @@ -90,12 +95,48 @@ paths: schema: $ref: "#/components/schemas/vm_list_response" + post: + summary: Deploy OVA file + description: Deploy OVA file + tags: + - Virtual Machines + parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/deploy_ova_body' + + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + task_id: + type: string + example: + "task_id": "548f65e9-2f79-2af9-8641-be75088f43c5" + /vm/{vm_uuid}: get: summary: "Get information about VM" tags: - Virtual Machines parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: vm_uuid in: path required: true @@ -123,6 +164,11 @@ paths: - Virtual Machines - Snapshots parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: vm_uuid in: path required: true @@ -148,6 +194,11 @@ paths: - Virtual Machines - Snapshots parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: vm_uuid in: path required: true @@ -181,6 +232,11 @@ paths: - Virtual Machines - Snapshots parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: vm_uuid in: path required: true @@ -207,6 +263,11 @@ paths: - Virtual Machines - Snapshots parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: vm_uuid in: path required: true @@ -233,6 +294,11 @@ paths: - Virtual Machines - Snapshots parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: vm_uuid in: path required: true @@ -259,6 +325,11 @@ paths: - Virtual Machines - Permissions parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: vm_uuid in: path required: true @@ -289,8 +360,8 @@ paths: tags: - Permissions parameters: - - in: header - name: X-Request-ID + - name: X-Request-ID + in: header schema: type: string format: uuid @@ -308,6 +379,11 @@ paths: tags: - Find parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid - name: path in: query required: true @@ -335,6 +411,33 @@ paths: schema: $ref: "#/components/schemas/find_vm_error_response" + /status/{task_id}: + get: + summary: "Get information about backgroud task" + tags: + - Tasks + parameters: + - name: X-Request-ID + in: header + schema: + type: string + format: uuid + - name: task_id + in: path + required: true + description: Task ID + schema: + type: string + format: uuid + example: 6ef18379-6220-6f7e-30ca-1d1c20a3cc97 + responses: + '200': + description: OK + content: + application/json:: + schema: + $ref: "#/components/schemas/task_id_response" + components: schemas: build_info_response: @@ -469,3 +572,67 @@ components: "description": "label": "Virtual machine user (sample)" "summary": "Provides virtual machine interaction permissions" + + task_id_response: + type: object + properties: + task_id: + type: string + example: + "status": "Starting deploy" + + deploy_ova_body: + type: object + properties: + name: + type: string + example: Janna VM + ova_url: + type: string + format: uri + example: https://stable.release.core-os.net/amd64-usr/current/coreos_production_vmware_ova.ova + datacenter: + type: string + example: DC1 + folder: + type: string + example: Dev VMs + annotation: + type: string + example: This is an annotation of the deployed VM + datastores: + type: object + properties: + type: + type: string + description: "Storage type. Possible values: 'cluster', 'datastore'. If 'cluster' type was chosen then Janna try to get DRS recommendation to choose proper 'datastore'." + example: cluster + names: + type: array + items: + type: string + description: List of storages. One of them will be chosen randomly. + example: DatastoreCluster1 + required: + - type + networks: + type: object + description: Network defines a mapping from each network inside the OVF to a ESXi network. The networks must be presented on the ESXi host. + example: + "VM Network": "esxi-net1" + computer_resources: + type: object + properties: + type: + type: string + description: "Computer resource type. Possible values: 'host', 'cluster', 'rp'." + example: cluster + path: + type: string + description: Name or path to a computer resource. The parameter Can be omitted, then Janna will try to get default computer resource. + example: my-esxi-cluster + required: + - type + required: + - name + - ova_url diff --git a/config/.env.example b/config/.env.example index 84de737..cf1f452 100644 --- a/config/.env.example +++ b/config/.env.example @@ -1,18 +1,20 @@ # Example configuration file -# Janna common settings +### Janna common settings PORT=8080 +DEBUG=false -# VMWare settings +# Task info Time-To-Live +TASKS_TTL=27 + +### VMware setting +# Connection settings VMWARE_URL=username@domain.local:password@vmware-address.com VMWARE_INSECURE=1 -# VMWare default parameters. May be overriden in API request. -VMWARE_DC=DC1 -VMWARE_DS=Cluster/host1 -VMWARE_FOLDER=vm-folder -VMWARE_RP=janna +### VMWare resources default parameters. May be overriden in API request. +# Datacenter +VMWARE_DATACENTER=DC1 -# VMWARE_HOST is optional. So if you use empty value, then vCenter will choose a host to deploy. -# If you need a specify a cluster then specify a Resource Pool (VMWARE_RP env) param. -# VMWARE_HOST=vi-devops-esx7.lab.vi.local +# Folder VMs deploy to +VMWARE_FOLDER=vm-folder diff --git a/internal/config/config.go b/internal/config/config.go index 519d053..3910a56 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -19,10 +19,7 @@ type resources struct { URL string Insecure bool DC string - DS string - RP string Folder string - Host string } type protocols struct { @@ -72,42 +69,18 @@ func Load() (*Config, error) { config.VMWare.URL = vmwareURL // VMWare Datacenter - vmwareDC, ok := os.LookupEnv("VMWARE_DC") + vmwareDC, ok := os.LookupEnv("VMWARE_DATACENTER") if !ok { - return nil, errors.New("provide 'VMWARE_DC' environment variable") + return nil, errors.New("provide 'VMWARE_DATACENTER' environment variable") } config.VMWare.DC = vmwareDC - // VMWare Datastore - vmwareDS, exist := os.LookupEnv("VMWARE_DS") - if !exist { - return nil, errors.New("provide 'VMWARE_DS' environment variable") - } - config.VMWare.DS = vmwareDS - - // VMWare Resource Pool - vmwareRP, exist := os.LookupEnv("VMWARE_RP") - if !exist { - return nil, errors.New("provide 'VMWARE_RP' environment variable") - } - config.VMWare.RP = vmwareRP - // VMWare VM Folder vmwareFolder, exist := os.LookupEnv("VMWARE_FOLDER") - if !exist { - config.VMWare.Folder = "" - } else { + if exist { config.VMWare.Folder = vmwareFolder } - // VMWare ESXi Host - vmwareHost, exist := os.LookupEnv("VMWARE_HOST") - if !exist { - config.VMWare.Host = "" - } else { - config.VMWare.Host = vmwareHost - } - // Background jobs time to live defaultTTL := time.Minute * 30 taskTTL, exist := os.LookupEnv("TASKS_TTL") diff --git a/internal/endpoint/vm_deploy.go b/internal/endpoint/vm_deploy.go index 5cdb79b..0841dbb 100644 --- a/internal/endpoint/vm_deploy.go +++ b/internal/endpoint/vm_deploy.go @@ -30,13 +30,28 @@ func MakeVMDeployEndpoint(s service.Service, logger log.Logger) endpoint.Endpoin params := &types.VMDeployParams{ Name: req.Name, OVAURL: req.OVAURL, - Datastores: req.Datastores, - Networks: req.Networks, Datacenter: req.Datacenter, - Cluster: req.Cluster, Folder: req.Folder, + Annotation: req.Annotation, + Networks: req.Networks, + Datastores: struct { + Type string + Names []string + }{ + Type: req.Datastores.Type, + Names: req.Datastores.Names, + }, + ComputerResources: struct { + Path string + Type string + }{ + Path: req.ComputerResources.Path, + Type: req.ComputerResources.Type, + }, } + params.FillEmptyFields(s.GetConfig()) + jid, err := s.VMDeploy(ctx, params) return VMDeployResponse{JID: jid, Err: err}, nil @@ -45,18 +60,29 @@ func MakeVMDeployEndpoint(s service.Service, logger log.Logger) endpoint.Endpoin // VMDeployRequest collects the request parameters for the VMDeploy method type VMDeployRequest struct { - Name string `json:"name"` - OVAURL string `json:"ova_url"` - Datastores []string `json:"datastores,omitempty"` - Networks map[string]string `json:"networks,omitempty"` - Datacenter string `json:"datacenter,omitempty"` - Cluster string `json:"cluster,omitempty"` - Folder string `json:"folder,omitempty"` + Name string `json:"name"` + OVAURL string `json:"ova_url"` + Datacenter string `json:"datacenter,omitempty"` + Folder string `json:"folder,omitempty"` + Annotation string `json:"annotation"` + Networks map[string]string `json:"networks,omitempty"` + Datastores `json:"datastores"` + ComputerResources `json:"computer_resources"` +} + +type Datastores struct { + Type string + Names []string +} + +type ComputerResources struct { + Path string `json:"path"` + Type string `json:"type"` } func (r *VMDeployRequest) String() string { - return fmt.Sprintf("name: %s, ova_url: %s, datastores: %s, networks: %s, datacenter: %s, cluster: %s, folder: %s", - r.Name, r.OVAURL, r.Datastores, r.Networks, r.Datacenter, r.Cluster, r.Folder) + return fmt.Sprintf("name: %s, ova_url: %s, datastores: %s, networks: %s, datacenter: %s, computer_resources: %s, folder: %s, annotation: %s", + r.Name, r.OVAURL, r.Datastores, r.Networks, r.Datacenter, r.ComputerResources, r.Folder, r.Annotation) } // VMDeployResponse fields diff --git a/internal/providers/vmware/vm/deploy.go b/internal/providers/vmware/vm/deploy.go index a86e84d..554c933 100644 --- a/internal/providers/vmware/vm/deploy.go +++ b/internal/providers/vmware/vm/deploy.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" "net/url" "os" "path" @@ -20,7 +21,9 @@ import ( "github.com/vmware/govmomi/nfc" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/ovf" + "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/progress" "github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/types" @@ -32,23 +35,25 @@ import ( type Deployment struct { Client *vim25.Client Finder *find.Finder - ovfx logger log.Logger + + ovfx } type ovfx struct { + // Name is the Virtual Machine name Name string Datacenter *object.Datacenter Datastore *object.Datastore - ResourcePool *object.ResourcePool Folder *object.Folder - Cluster *object.ClusterComputeResource + ResourcePool *object.ResourcePool Host *object.HostSystem NetworkMapping []Network + Annotation string } -// Network represent mapping between OVF network and ESXi system network. -// 'OVF-VM-Network-Name' -> 'Yours-ESXi-VM-Network-Name' +// Network defines a mapping from each network inside the OVF +// to a ESXi network. The networks must be presented on the ESXi host. type Network struct { Name string Network string @@ -64,26 +69,105 @@ func (o *Deployment) chooseDatacenter(ctx context.Context, dcName string) error return nil } -func (o *Deployment) chooseDatastore(ctx context.Context, dsName string) error { - // TODO: try to use DatastoreCLuster instead of Datastore - // user can choose that want to use - ds, err := o.Finder.DatastoreOrDefault(ctx, dsName) +func (o *Deployment) chooseDatastore(ctx context.Context, dsType string, names []string) error { + switch dsType { + case "cluster": + if err := o.chooseDatastoreWithCluster(ctx, names); err != nil { + return err + } + case "datastore": + if err := o.chooseDatastoreWithDatastore(ctx, names); err != nil { + return err + } + default: + errors.New("could not recognize datastore type. Possible values are 'cluster', 'datastore'") + } + return nil +} + +func (o *Deployment) chooseDatastoreWithCluster(ctx context.Context, names []string) error { + pod, err := o.Finder.DatastoreClusterOrDefault(ctx, pickRandom(names)) if err != nil { return err } - o.Datastore = ds + + drsEnabled, err := isStorageDRSEnabled(ctx, pod) + if err != nil { + return err + } + if !drsEnabled { + return errors.New("storage DRS is not enabled on datastore cluster") + } + + var vmSpec types.VirtualMachineConfigSpec + sps := types.StoragePlacementSpec{ + Type: string(types.StoragePlacementSpecPlacementTypeCreate), + ResourcePool: types.NewReference(o.ResourcePool.Reference()), + PodSelectionSpec: types.StorageDrsPodSelectionSpec{ + StoragePod: types.NewReference(pod.Reference()), + }, + Folder: types.NewReference(o.Folder.Reference()), + ConfigSpec: &vmSpec, + } + + o.logger.Log("msg", "Acquiring Storage DRS recommendations") + srm := object.NewStorageResourceManager(o.Client) + placement, err := srm.RecommendDatastores(ctx, sps) + if err != nil { + return err + } + + recs := placement.Recommendations + if len(recs) < 1 { + return errors.New("no storage DRS recommendations were found for the requested action") + } + + spa, ok := recs[0].Action[0].(*types.StoragePlacementAction) + if !ok { + return errors.New("could not get datastore from DRS recomendation") + } + + ds := spa.Destination + var mds mo.Datastore + err = property.DefaultCollector(o.Client).RetrieveOne(ctx, ds, []string{"name"}, &mds) + if err != nil { + return err + } + + datastore := object.NewDatastore(o.Client, ds) + + o.Datastore = datastore return nil } -func (o *Deployment) chooseResourcePool(ctx context.Context, rpName string) error { - rp, err := o.Finder.ResourcePoolOrDefault(ctx, rpName) +func isStorageDRSEnabled(ctx context.Context, pod *object.StoragePod) (bool, error) { + var props mo.StoragePod + if err := pod.Properties(ctx, pod.Reference(), nil, &props); err != nil { + return false, err + } + + if props.PodStorageDrsEntry == nil { + return false, nil + } + + return props.PodStorageDrsEntry.StorageDrsConfig.PodConfig.Enabled, nil +} + +func (o *Deployment) chooseDatastoreWithDatastore(ctx context.Context, names []string) error { + ds, err := o.Finder.DatastoreOrDefault(ctx, pickRandom(names)) if err != nil { return err } - o.ResourcePool = rp + + o.Datastore = ds return nil } +func pickRandom(slice []string) string { + rand.Seed(time.Now().Unix()) + return slice[rand.Intn(len(slice))] +} + func (o *Deployment) chooseFolder(ctx context.Context, fName string) error { folder, err := o.Finder.FolderOrDefault(ctx, fName) if err != nil { @@ -93,19 +177,71 @@ func (o *Deployment) chooseFolder(ctx context.Context, fName string) error { return nil } -func (o *Deployment) chooseHost(ctx context.Context, hName string) error { - // Host param is optional. If we use 'nil', then vCenter will choose a host - // If you need a specify a cluster then specify a Resource Pool param. - if hName == "" { - o.Host = nil - return nil +func (o *Deployment) chooseComputerResource(ctx context.Context, resType, path string) error { + switch resType { + case "host": + if err := o.computerResourceWithHost(ctx, path); err != nil { + return err + } + case "cluster": + if err := o.computerResourceWithCluster(ctx, path); err != nil { + return err + } + case "rp": + if err := o.computerResourceWithResourcePool(ctx, path); err != nil { + return err + } + default: + return errors.New("could not recognize computer resource type. Possible types are 'host', 'cluster', 'rp'") + } + + return nil +} + +func (o *Deployment) computerResourceWithHost(ctx context.Context, path string) error { + host, err := o.Finder.HostSystemOrDefault(ctx, path) + if err != nil { + return err } - host, err := o.Finder.HostSystem(ctx, hName) + rp, err := host.ResourcePool(ctx) if err != nil { return err } + o.Host = host + o.ResourcePool = rp + return nil +} + +func (o *Deployment) computerResourceWithCluster(ctx context.Context, path string) error { + cluster, err := o.Finder.ClusterComputeResourceOrDefault(ctx, path) + if err != nil { + return err + } + + rp, err := cluster.ResourcePool(ctx) + if err != nil { + return err + } + + o.ResourcePool = rp + + // vCenter will choose a host + o.Host = nil + return nil +} + +func (o *Deployment) computerResourceWithResourcePool(ctx context.Context, rpName string) error { + rp, err := o.Finder.ResourcePoolOrDefault(ctx, rpName) + if err != nil { + return err + } + + o.ResourcePool = rp + + // vCenter will choose a host + o.Host = nil return nil } @@ -164,7 +300,7 @@ func (o *Deployment) Upload(ctx context.Context, lease *nfc.Lease, item nfc.File return lease.Upload(ctx, item, f, opts) } -func (o *Deployment) Import(ctx context.Context, OVAURL string) (*types.ManagedObjectReference, error) { +func (o *Deployment) Import(ctx context.Context, OVAURL string, anno string) (*types.ManagedObjectReference, error) { url, err := url.Parse(OVAURL) if err != nil { return nil, err @@ -239,18 +375,18 @@ func (o *Deployment) Import(ctx context.Context, OVAURL string) (*types.ManagedO } m := ovf.NewManager(o.Client) + ovfContent := string(b) rp := o.ResourcePool ds := o.Datastore - spec, err := m.CreateImportSpec(ctx, string(b), rp, ds, cisp) + spec, err := m.CreateImportSpec(ctx, ovfContent, rp, ds, cisp) if err != nil { return nil, errors.Wrap(err, "Could not create VM spec") } if spec.Error != nil { + o.logger.Log("err", spec.Error[0].LocalizedMessage) return nil, errors.New(spec.Error[0].LocalizedMessage) } - // TODO: get from params - anno := "Test annotations" if anno != "" { switch s := spec.ImportSpec.(type) { case *types.VirtualMachineImportSpec: @@ -262,11 +398,15 @@ func (o *Deployment) Import(ctx context.Context, OVAURL string) (*types.ManagedO lease, err := rp.ImportVApp(ctx, spec.ImportSpec, o.Folder, o.Host) if err != nil { - return nil, errors.Wrap(err, "Could not import Virtual Appliance") + err = errors.Wrap(err, "Could not import Virtual Appliance") + o.logger.Log("err", err) + return nil, err } info, err := lease.Wait(ctx, spec.FileItem) if err != nil { + err = errors.Wrap(err, "error while waiting lease") + o.logger.Log("err", err) return nil, err } @@ -304,32 +444,34 @@ func IsVMExist(ctx context.Context, c *vim25.Client, params *jt.VMDeployParams) // It choose needed resources func NewDeployment(ctx context.Context, c *vim25.Client, params *jt.VMDeployParams, l log.Logger, cfg *config.Config) (*Deployment, error) { // nolint: unparam d := newSimpleDeployment(c, params, l) - if err := d.chooseDatacenter(ctx, cfg.VMWare.DC); err != nil { - err = errors.Wrap(err, "Could not choose datacenter") - l.Log("err", err) - return nil, err - } - if err := d.chooseDatastore(ctx, cfg.VMWare.DS); err != nil { - err = errors.Wrap(err, "Could not choose datastore") + // step 1. choose Datacenter and folder + if err := d.chooseDatacenter(ctx, params.Datacenter); err != nil { + err = errors.Wrap(err, "Could not choose datacenter") l.Log("err", err) return nil, err } - if err := d.chooseResourcePool(ctx, cfg.VMWare.RP); err != nil { - err = errors.Wrap(err, "Could not choose resource pool") + if err := d.chooseFolder(ctx, params.Folder); err != nil { + err = errors.Wrap(err, "Could not choose folder") l.Log("err", err) return nil, err } - if err := d.chooseFolder(ctx, cfg.VMWare.Folder); err != nil { - err = errors.Wrap(err, "Could not choose folder") + // step 2. choose computer resource + resType := params.ComputerResources.Type + resPath := params.ComputerResources.Path + if err := d.chooseComputerResource(ctx, resType, resPath); err != nil { + err = errors.Wrap(err, "Could not choose Computer Resource") l.Log("err", err) return nil, err } - if err := d.chooseHost(ctx, cfg.VMWare.Host); err != nil { - err = errors.Wrap(err, "Could not choose host") + // step 3. Choose datastore cluster or single datastore + dsType := params.Datastores.Type + dsNames := params.Datastores.Names + if err := d.chooseDatastore(ctx, dsType, dsNames); err != nil { + err = errors.Wrap(err, "Could not choose datastore") l.Log("err", err) return nil, err } @@ -428,10 +570,11 @@ func (p *progressLogger) loopA() { } } - // TODO: change to using logger if err != nil && err != io.EOF { p.logger.Log("err", errors.Wrap(err, "Error with disks uploading"), "file", p.prefix) - } else if called { + } + + if called { p.logger.Log("msg", "uploaded", "file", p.prefix) } } diff --git a/internal/service/service.go b/internal/service/service.go index 0b20263..e707eed 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -171,7 +171,7 @@ func (s *service) VMDeploy(ctx context.Context, params *types.VMDeployParams) (s } s.statuses.Add(taskID, "Importing OVA") - moref, err := d.Import(taskCtx, params.OVAURL) + moref, err := d.Import(taskCtx, params.OVAURL, params.Annotation) if err != nil { err = errors.Wrap(err, "Could not import OVA/OVF") l.Log("err", err) diff --git a/internal/transport/http.go b/internal/transport/http.go index c6265bd..66b7f18 100644 --- a/internal/transport/http.go +++ b/internal/transport/http.go @@ -131,8 +131,8 @@ func NewHTTPHandler(endpoints endpoint.Endpoints, logger log.Logger) http.Handle options..., )) - // Statuses - r.Path("/status/{taskID}").Methods("GET").Handler(httptransport.NewServer( + // Tasks statuses + r.Path("/tasks/{taskID}").Methods("GET").Handler(httptransport.NewServer( endpoints.TaskInfoEndpoint, decodeTaskInfoRequest, encodeResponse, diff --git a/internal/types/params.go b/internal/types/params.go index 3700ff7..d59201c 100644 --- a/internal/types/params.go +++ b/internal/types/params.go @@ -22,28 +22,6 @@ func (p *VMListParams) FillEmptyFields(cfg *config.Config) { } } -// VMDeployParams stores user request params -type VMDeployParams struct { - Name string - OVAURL string - Datastores []string - Networks map[string]string - Datacenter string - Cluster string - Folder string -} - -// FillEmptyFields stores default parameters to the struct if some fields was empty -func (p *VMDeployParams) FillEmptyFields(cfg *config.Config) { - if p.Datacenter == "" { - p.Datacenter = cfg.VMWare.DC - } - - // TODO: add default datastores - // if p.Datastores == nil { - // } -} - // SnapshotCreateParams stores user request params type SnapshotCreateParams struct { UUID string diff --git a/internal/types/params_deploy.go b/internal/types/params_deploy.go new file mode 100644 index 0000000..aeafa7a --- /dev/null +++ b/internal/types/params_deploy.go @@ -0,0 +1,35 @@ +package types + +import ( + "github.com/vterdunov/janna-api/internal/config" +) + +// VMDeployParams stores user request params +type VMDeployParams struct { + Name string + OVAURL string + Datacenter string + Folder string + Annotation string + Networks map[string]string + ComputerResources struct { + Path string + Type string + } + Datastores struct { + Type string + Names []string + } +} + +// FillEmptyFields stores default parameters to the struct if some fields was empty +func (p *VMDeployParams) FillEmptyFields(cfg *config.Config) { + if p.Datacenter == "" { + p.Datacenter = cfg.VMWare.DC + } + + if p.Folder == "" { + p.Folder = cfg.VMWare.Folder + } + +}