Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add default inventory #116

Merged
merged 1 commit into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/planner-api/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ var runCmd = &cobra.Command{
zap.S().Fatalf("running initial migration: %v", err)
}

// Initialize database with basic example report
if v, b := os.LookupEnv("NO_SEED"); !b || v == "false" {
if err := store.Seed(); err != nil {
zap.S().Fatalf("seeding database with default report: %v", err)
}
}

// initilize event writer
ep, _ := getEventProducer(cfg)

Expand Down
12 changes: 10 additions & 2 deletions internal/service/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@ import (
)

func (h *ServiceHandler) ListAgents(ctx context.Context, request server.ListAgentsRequestObject) (server.ListAgentsResponseObject, error) {
// Get user content:
qf := store.NewAgentQueryFilter()
if user, found := auth.UserFromContext(ctx); found {
qf = qf.ByUsername(user.Username)
}
result, err := h.store.Agent().List(ctx, qf, store.NewAgentQueryOptions().WithIncludeSoftDeleted(false))
userResult, err := h.store.Agent().List(ctx, qf, store.NewAgentQueryOptions().WithIncludeSoftDeleted(false))
if err != nil {
return nil, err
}
return server.ListAgents200JSONResponse(mappers.AgentListToApi(result)), nil

// Get default content
defaultResult, err := h.store.Agent().List(ctx, store.NewAgentQueryFilter().ByDefaultInventory(), store.NewAgentQueryOptions().WithIncludeSoftDeleted(false))
if err != nil {
return nil, err
}

return server.ListAgents200JSONResponse(mappers.AgentListToApi(userResult, defaultResult)), nil
}

func (h *ServiceHandler) DeleteAgent(ctx context.Context, request server.DeleteAgentRequestObject) (server.DeleteAgentResponseObject, error) {
Expand Down
2 changes: 2 additions & 0 deletions internal/service/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ var _ = Describe("agent handler", Ordered, func() {

AfterEach(func() {
gormdb.Exec("DELETE FROM agents;")
gormdb.Exec("DELETE FROM sources;")
})
})

Expand Down Expand Up @@ -182,6 +183,7 @@ var _ = Describe("agent handler", Ordered, func() {

AfterEach(func() {
gormdb.Exec("DELETE FROM agents;")
gormdb.Exec("DELETE FROM sources;")
})
})
})
16 changes: 10 additions & 6 deletions internal/service/mappers/outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ func SourceToApi(s model.Source) api.Source {
return source
}

func SourceListToApi(sources model.SourceList) api.SourceList {
sourceList := make([]api.Source, 0, len(sources))
func SourceListToApi(sources ...model.SourceList) api.SourceList {
sourceList := []api.Source{}
for _, source := range sources {
sourceList = append(sourceList, SourceToApi(source))
for _, s := range source {
sourceList = append(sourceList, SourceToApi(s))
}
}
return sourceList
}
Expand Down Expand Up @@ -59,10 +61,12 @@ func AgentToApi(a model.Agent) api.Agent {
return agent
}

func AgentListToApi(agents model.AgentList) api.AgentList {
agentList := make([]api.Agent, 0, len(agents))
func AgentListToApi(agents ...model.AgentList) api.AgentList {
agentList := []api.Agent{}
for _, agent := range agents {
agentList = append(agentList, AgentToApi(agent))
for _, a := range agent {
agentList = append(agentList, AgentToApi(a))
}
}
return agentList
}
12 changes: 10 additions & 2 deletions internal/service/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ import (
)

func (h *ServiceHandler) ListSources(ctx context.Context, request server.ListSourcesRequestObject) (server.ListSourcesResponseObject, error) {
// Get user content
filter := store.NewSourceQueryFilter()
if user, found := auth.UserFromContext(ctx); found {
filter = filter.ByUsername(user.Username)
}
result, err := h.store.Source().List(ctx, filter)
userResult, err := h.store.Source().List(ctx, filter)
if err != nil {
return nil, err
}
return server.ListSources200JSONResponse(mappers.SourceListToApi(result)), nil

// Get default content
defaultResult, err := h.store.Source().List(ctx, store.NewSourceQueryFilter().ByDefaultInventory())
if err != nil {
return nil, err
}

return server.ListSources200JSONResponse(mappers.SourceListToApi(userResult, defaultResult)), nil
}

func (h *ServiceHandler) DeleteSources(ctx context.Context, request server.DeleteSourcesRequestObject) (server.DeleteSourcesResponseObject, error) {
Expand Down
172 changes: 172 additions & 0 deletions internal/store/inventory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package store

import (
api "github.com/kubev2v/migration-planner/api/v1alpha1"
)

func GenerateDefaultInventory() api.Inventory {
n := 107
dvSwitch := "management"
dvSwitchVm := "vm"
vlanid100 := "100"
vlanid200 := "200"
vlanTrunk := "0-4094"
return api.Inventory{
Infra: api.Infra{
Datastores: []struct {
FreeCapacityGB int `json:"freeCapacityGB"`
TotalCapacityGB int `json:"totalCapacityGB"`
Type string `json:"type"`
}{
{FreeCapacityGB: 615, TotalCapacityGB: 766, Type: "VMFS"},
{FreeCapacityGB: 650, TotalCapacityGB: 766, Type: "VMFS"},
{FreeCapacityGB: 167, TotalCapacityGB: 221, Type: "VMFS"},
{FreeCapacityGB: 424, TotalCapacityGB: 766, Type: "VMFS"},
{FreeCapacityGB: 1369, TotalCapacityGB: 3321, Type: "VMFS"},
{FreeCapacityGB: 1252, TotalCapacityGB: 3071, Type: "VMFS"},
{FreeCapacityGB: 415, TotalCapacityGB: 766, Type: "VMFS"},
{FreeCapacityGB: 585, TotalCapacityGB: 766, Type: "VMFS"},
{FreeCapacityGB: 170, TotalCapacityGB: 196, Type: "NFS"},
{FreeCapacityGB: 606, TotalCapacityGB: 766, Type: "VMFS"},
{FreeCapacityGB: 740, TotalCapacityGB: 766, Type: "VMFS"},
},
HostPowerStates: map[string]int{
"Green": 8,
},
HostsPerCluster: []int{1, 7, 0},
Networks: []struct {
Dvswitch *string `json:"dvswitch,omitempty"`
Name string `json:"name"`
Type api.InfraNetworksType `json:"type"`
VlanId *string `json:"vlanId,omitempty"`
}{
{Name: dvSwitch, Type: "dvswitch"},
{Dvswitch: &dvSwitch, Name: "mgmt", Type: "distributed", VlanId: &vlanid100},
{Name: dvSwitchVm, Type: "dvswitch"},
{Dvswitch: &dvSwitchVm, Name: "storage", Type: "distributed", VlanId: &vlanid200},
{Dvswitch: &dvSwitch, Name: "vMotion", Type: "distributed", VlanId: &vlanid100},
{Dvswitch: &dvSwitch, Name: "trunk", Type: "distributed", VlanId: &vlanTrunk},
},
TotalClusters: 3,
TotalHosts: 8,
},
Vcenter: api.VCenter{
Id: "00000000-0000-0000-0000-000000000000",
},
Vms: api.VMs{
CpuCores: api.VMResourceBreakdown{
Histogram: struct {
Data []int `json:"data"`
MinValue int `json:"minValue"`
Step int `json:"step"`
}{
Data: []int{45, 0, 39, 2, 13, 0, 0, 0, 0, 8},
MinValue: 1,
Step: 2,
},
Total: 472,
TotalForMigratableWithWarnings: 472,
},
DiskCount: api.VMResourceBreakdown{
Histogram: struct {
Data []int `json:"data"`
MinValue int `json:"minValue"`
Step int `json:"step"`
}{
Data: []int{10, 91, 1, 2, 0, 2, 0, 0, 0, 1},
MinValue: 0,
Step: 1,
},
Total: 115,
TotalForMigratableWithWarnings: 115,
},
NotMigratableReasons: []struct {
Assessment string `json:"assessment"`
Count int `json:"count"`
Label string `json:"label"`
}{},
DiskGB: api.VMResourceBreakdown{
Histogram: struct {
Data []int `json:"data"`
MinValue int `json:"minValue"`
Step int `json:"step"`
}{
Data: []int{32, 23, 31, 14, 0, 2, 2, 1, 0, 2},
MinValue: 0,
Step: 38,
},
Total: 7945,
TotalForMigratableWithWarnings: 7945,
},
RamGB: api.VMResourceBreakdown{
Histogram: struct {
Data []int `json:"data"`
MinValue int `json:"minValue"`
Step int `json:"step"`
}{
Data: []int{49, 32, 1, 14, 0, 0, 9, 0, 0, 2},
MinValue: 1,
Step: 5,
},
Total: 1031,
TotalForMigratableWithWarnings: 1031,
},
PowerStates: map[string]int{
"poweredOff": 78,
"poweredOn": 29,
},
Os: map[string]int{
"Amazon Linux 2 (64-bit)": 1,
"CentOS 7 (64-bit)": 1,
"CentOS 8 (64-bit)": 1,
"Debian GNU/Linux 12 (64-bit)": 1,
"FreeBSD (64-bit)": 2,
"Microsoft Windows 10 (64-bit)": 2,
"Microsoft Windows 11 (64-bit)": 2,
"Microsoft Windows Server 2019 (64-bit)": 8,
"Microsoft Windows Server 2022 (64-bit)": 3,
"Microsoft Windows Server 2025 (64-bit)": 2,
"Other (32-bit)": 12,
"Other (64-bit)": 1,
"Other 2.6.x Linux (64-bit)": 13,
"Other Linux (64-bit)": 1,
"Red Hat Enterprise Linux 8 (64-bit)": 5,
"Red Hat Enterprise Linux 9 (64-bit)": 41,
"Red Hat Fedora (64-bit)": 2,
"Rocky Linux (64-bit)": 1,
"Ubuntu Linux (64-bit)": 3,
"VMware ESXi 8.0 or later": 5,
},
MigrationWarnings: api.MigrationIssues{
{
Label: "Changed Block Tracking (CBT) not enabled",
Count: 105,
Assessment: "Changed Block Tracking (CBT) has not been enabled on this VM. This feature is a prerequisite for VM warm migration.",
},
{
Label: "UEFI detected",
Count: 77,
Assessment: "UEFI secure boot will be disabled on OpenShift Virtualization. If the VM was set with UEFI secure boot, manual steps within the guest would be needed for the guest operating system to boot.",
},
{
Label: "Invalid VM Name",
Count: 31,
Assessment: "The VM name must comply with the DNS subdomain name format defined in RFC 1123. The name can contain lowercase letters (a-z), numbers (0-9), and hyphens (-), up to a maximum of 63 characters. The first and last characters must be alphanumeric. The name must not contain uppercase letters, spaces, periods (.), or special characters. The VM will be renamed automatically during the migration to meet the RFC convention.",
},
{
Label: "VM configured with a TPM device",
Count: 3,
Assessment: "The VM is configured with a TPM device. TPM data is not transferred during the migration.",
},
{
Label: "Independent disk detected",
Count: 2,
Assessment: "Independent disks cannot be transferred using recent versions of VDDK. It is recommended to change them in vSphere to 'Dependent' mode, or alternatively, to export the VM to an OVA.",
},
},
Total: 107,
TotalMigratable: 107,
TotalMigratableWithWarnings: &n,
},
}
}
19 changes: 18 additions & 1 deletion internal/store/options.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package store

import "gorm.io/gorm"
import (
"github.com/google/uuid"
"gorm.io/gorm"
)

type BaseQuerier struct {
QueryFn []func(tx *gorm.DB) *gorm.DB
Expand Down Expand Up @@ -33,6 +36,13 @@ func (qf *AgentQueryFilter) ByOrgID(id string) *AgentQueryFilter {
return qf
}

func (qf *AgentQueryFilter) ByDefaultInventory() *AgentQueryFilter {
qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB {
return tx.Where("id = ?", uuid.UUID{})
})
return qf
}

func (qf *AgentQueryFilter) BySoftDeleted(isSoftDeleted bool) *AgentQueryFilter {
qf.QueryFn = append(qf.QueryFn, func(tx *gorm.DB) *gorm.DB {
if isSoftDeleted {
Expand Down Expand Up @@ -101,3 +111,10 @@ func (sf *SourceQueryFilter) ByOrgID(id string) *SourceQueryFilter {
})
return sf
}

func (sf *SourceQueryFilter) ByDefaultInventory() *SourceQueryFilter {
sf.QueryFn = append(sf.QueryFn, func(tx *gorm.DB) *gorm.DB {
return tx.Where("id = ?", uuid.UUID{})
})
return sf
}
47 changes: 47 additions & 0 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ package store
import (
"context"

"github.com/google/uuid"
api "github.com/kubev2v/migration-planner/api/v1alpha1"
"github.com/kubev2v/migration-planner/internal/store/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)

type Store interface {
NewTransactionContext(ctx context.Context) (context.Context, error)
Agent() Agent
Source() Source
Seed() error
InitialMigration() error
Close() error
}
Expand Down Expand Up @@ -58,6 +63,48 @@ func (s *DataStore) InitialMigration() error {
return err
}

func (s *DataStore) Seed() error {
sourceUuid := uuid.UUID{}
sourceUuidStr := sourceUuid.String()

tx, err := newTransaction(s.db)
if err != nil {
return err
}
// Create/update default source
source := model.Source{
ID: sourceUuid,
Inventory: model.MakeJSONField(GenerateDefaultInventory()),
}

if err := tx.tx.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&source).Error; err != nil {
_ = tx.Rollback()
}

// Create/update default agent
agent := model.Agent{
ID: sourceUuidStr,
Status: string(api.AgentStatusUpToDate),
StatusInfo: "Inventory successfully collected",
CredUrl: "Example report",
SourceID: &sourceUuidStr,
Associated: true,
}
if err := tx.tx.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&agent).Error; err != nil {
_ = tx.Rollback()
}

if err := tx.Commit(); err != nil {
return err
}

return nil
}

func (s *DataStore) Close() error {
sqlDB, err := s.db.DB()
if err != nil {
Expand Down
Loading
Loading