Skip to content

Commit

Permalink
Merge PR: Add default inventory kubev2v#116
Browse files Browse the repository at this point in the history
Signed-off-by: Aviel Segev <[email protected]>
  • Loading branch information
AvielSegev committed Jan 13, 2025
2 parents 58dade7 + 3b204bc commit 67d5a78
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 34 deletions.
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

0 comments on commit 67d5a78

Please sign in to comment.