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

[WIP] Device entity managment #57

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
130 changes: 130 additions & 0 deletions src/app/drivers/DevicePOEPowerDriver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package drivers

import (
"context"
"github.com/google/uuid"
"rol/app/errors"
"rol/app/interfaces"
"rol/domain"
)

//DevicePOEPowerDriver - power control driver for devices that uses POE as power supply
type DevicePOEPowerDriver struct {
provider interfaces.IEthernetSwitchManagerProvider
devEthRepo interfaces.IGenericRepository[uuid.UUID, domain.DeviceNetworkInterface]
switchPortsRepo interfaces.IGenericRepository[uuid.UUID, domain.EthernetSwitchPort]
devPowerHistory interfaces.IGenericRepository[uuid.UUID, domain.DevicePowerState]
}

//NewDevicePOEPowerDriver - constructor for DevicePOEPowerDriver
func NewDevicePOEPowerDriver(
provider interfaces.IEthernetSwitchManagerProvider,
devEthRepo interfaces.IGenericRepository[uuid.UUID, domain.DeviceNetworkInterface],
switchPortsRepo interfaces.IGenericRepository[uuid.UUID, domain.EthernetSwitchPort],
devPowerHistory interfaces.IGenericRepository[uuid.UUID, domain.DevicePowerState],
) *DevicePOEPowerDriver {
return &DevicePOEPowerDriver{
provider: provider,
devEthRepo: devEthRepo,
switchPortsRepo: switchPortsRepo,
devPowerHistory: devPowerHistory,
}
}

func (p *DevicePOEPowerDriver) getDeviceEthPort(ctx context.Context, device domain.Device) (domain.DeviceNetworkInterface, error) {
devPort := domain.DeviceNetworkInterface{}
devPorts, err := p.devEthRepo.GetList(ctx, "", "", 1, 1, nil)
if err != nil {
return devPort, errors.Internal.Wrapf(err, "failed to get ethernet ports for device: %s", device.ID)
}
if len(devPorts) < 1 {
return devPort, errors.Internal.Wrapf(err, "device with id: %s not have POE ports", device.ID)
}
return devPorts[0], nil
}

func (p *DevicePOEPowerDriver) getConnectedSwitchPort(ctx context.Context, devPort domain.DeviceNetworkInterface) (domain.EthernetSwitchPort, error) {
ethPortQuery := p.switchPortsRepo.NewQueryBuilder(ctx)
ethPortQuery.Where("EthernetSwitchID", "==", devPort.ConnectedSwitchId)
switchPort, err := p.switchPortsRepo.GetByIDExtended(ctx, devPort.ConnectedSwitchPortId, ethPortQuery)
if err != nil {
return domain.EthernetSwitchPort{}, errors.Internal.Wrapf(err, "failed to get connected switch port for device: %s", devPort.DeviceID)
}
return switchPort, nil
}

func (p *DevicePOEPowerDriver) getSwitchPortNameAndManager(ctx context.Context, dev domain.Device) (
string, interfaces.IEthernetSwitchManager, error,
) {
devEthPort, err := p.getDeviceEthPort(ctx, dev)
if err != nil {
return "", nil, err
}
switchEthPort, err := p.getConnectedSwitchPort(ctx, devEthPort)
if err != nil {
return "", nil, errors.Internal.Wrapf(err, "get power state of device with id %s failed", dev.ID.String())
}
switchManager, err := p.provider.Get(ctx, devEthPort.ConnectedSwitchId)
if err != nil {
return "", nil, errors.Internal.Wrapf(err, "get ethernet switch provider failed for device: %s", dev.ID.String())
}
return switchEthPort.Name, switchManager, nil
}

//GetPowerState - get device power status from power supply controller
func (p *DevicePOEPowerDriver) GetPowerState(ctx context.Context, dev domain.Device) (domain.DevPowerState, error) {
devUnkState := domain.DevInPowerUnknownState
portName, switchManager, err := p.getSwitchPortNameAndManager(ctx, dev)
if err != nil {
return devUnkState, err
}
poeStatus, err := switchManager.GetPOEPortStatus(portName)
if err != nil {
return devUnkState, errors.Internal.Wrapf(err, "failed to get power state of device with id: %s", dev.ID.String())
}
switch poeStatus {
case "enable":
return domain.DevInPowerOnState, nil
}
return domain.DevInPowerOffState, nil
}

//PowerOff device
func (p *DevicePOEPowerDriver) PowerOff(ctx context.Context, dev domain.Device) error {
portName, switchManager, err := p.getSwitchPortNameAndManager(ctx, dev)
if err != nil {
return err
}
err = switchManager.DisablePOEPort(portName)
if err != nil {
return errors.Internal.Wrapf(err, "disable POE power for device %s failed", dev.ID)
}
_, err = p.devPowerHistory.Insert(ctx, domain.DevicePowerState{
DeviceID: dev.ID,
DevPowerState: domain.DevInPowerOffState,
})
if err != nil {
return errors.Internal.Wrapf(err, "save POE power history for device %s failed", dev.ID)
}
return nil
}

//PowerOn device
func (p *DevicePOEPowerDriver) PowerOn(ctx context.Context, dev domain.Device) error {
portName, switchManager, err := p.getSwitchPortNameAndManager(ctx, dev)
if err != nil {
return err
}
err = switchManager.EnablePOEPort(portName, "poe+")
if err != nil {
return errors.Internal.Wrapf(err, "enable POE power for device %s failed", dev.ID)
}
_, err = p.devPowerHistory.Insert(ctx, domain.DevicePowerState{
DeviceID: dev.ID,
DevPowerState: domain.DevInPowerOnState,
})
if err != nil {
return errors.Internal.Wrapf(err, "save POE power history for device %s failed", dev.ID)
}
return nil
}
12 changes: 12 additions & 0 deletions src/app/interfaces/IDevicePowerDriver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package interfaces

import (
"context"
"rol/domain"
)

type IDevicePowerDriver interface {
GetPowerState(ctx context.Context, device domain.Device) (domain.DevPowerState, error)
PowerOn(ctx context.Context, device domain.Device) error
PowerOff(ctx context.Context, device domain.Device) error
}
39 changes: 39 additions & 0 deletions src/app/mappers/DeviceMapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Package mappers uses for entity <--> dto conversions
package mappers

import (
"github.com/google/uuid"
"rol/domain"
"rol/dtos"
)

//MapDeviceToDto writes Device entity to dto
//Params
// entity - Device entity
// dto - dest Device dto
func MapDeviceToDto(entity domain.Device, dto *dtos.DeviceDto) {
mapEntityToBaseDto[uuid.UUID](entity, &dto.BaseDto)
dto.Name = entity.Name
dto.Model = entity.Model
dto.PowerControlBus = entity.PowerControlBus
}

//MapDeviceCreateDtoToEntity writes Device create dto fields to entity
//Params
// dto - Device create dto
// entity - dest Device entity
func MapDeviceCreateDtoToEntity(dto dtos.DeviceCreateDto, entity *domain.Device) {
entity.Name = dto.Name
entity.Model = dto.Model
entity.PowerControlBus = dto.PowerControlBus
}

//MapDeviceUpdateDtoToEntity writes Device update dto fields to entity
//Params
// dto - Device update dto
// entity - dest Device entity
func MapDeviceUpdateDtoToEntity(dto dtos.DeviceUpdateDto, entity *domain.Device) {
entity.Name = dto.Name
entity.Model = dto.Model
entity.PowerControlBus = dto.PowerControlBus
}
39 changes: 39 additions & 0 deletions src/app/mappers/DeviceNetworkInterfaceMapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Package mappers uses for entity <--> dto conversions
package mappers

import (
"github.com/google/uuid"
"rol/domain"
"rol/dtos"
)

//MapDeviceNetworkInterfaceToDto writes DeviceNetworkInterface entity to dto
//Params
// entity - DeviceNetworkInterface entity
// dto - dest DeviceNetworkInterface dto
func MapDeviceNetworkInterfaceToDto(entity domain.DeviceNetworkInterface, dto *dtos.DeviceNetworkInterfaceDto) {
mapEntityToBaseDto[uuid.UUID](entity, &dto.BaseDto)
dto.Mac = entity.Mac
dto.ConnectedSwitchPortId = entity.ConnectedSwitchPortId
dto.ConnectedSwitchId = entity.ConnectedSwitchId
}

//MapDeviceNetworkInterfaceCreateDtoToEntity writes DeviceNetworkInterface create dto fields to entity
//Params
// dto - DeviceNetworkInterface create dto
// entity - dest DeviceNetworkInterface entity
func MapDeviceNetworkInterfaceCreateDtoToEntity(dto dtos.DeviceNetworkInterfaceCreateDto, entity *domain.DeviceNetworkInterface) {
entity.Mac = dto.Mac
entity.ConnectedSwitchId = dto.ConnectedSwitchId
entity.ConnectedSwitchPortId = dto.ConnectedSwitchPortId
}

//MapDeviceNetworkInterfaceUpdateDtoToEntity writes DeviceNetworkInterface update dto fields to entity
//Params
// dto - DeviceNetworkInterface update dto
// entity - dest DeviceNetworkInterface entity
func MapDeviceNetworkInterfaceUpdateDtoToEntity(dto dtos.DeviceNetworkInterfaceUpdateDto, entity *domain.DeviceNetworkInterface) {
entity.Mac = dto.Mac
entity.ConnectedSwitchId = dto.ConnectedSwitchId
entity.ConnectedSwitchPortId = dto.ConnectedSwitchPortId
}
17 changes: 16 additions & 1 deletion src/app/mappers/EntityDtoMapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ func MapDtoToEntity(dto interface{}, entity interface{}) error {
MapDHCP4LeaseCreateDtoToEntity(dto.(dtos.DHCP4LeaseCreateDto), entity.(*domain.DHCP4Lease))
case dtos.DHCP4LeaseUpdateDto:
MapDHCP4LeaseUpdateDtoToEntity(dto.(dtos.DHCP4LeaseUpdateDto), entity.(*domain.DHCP4Lease))
//Device
case dtos.DeviceCreateDto:
MapDeviceCreateDtoToEntity(dto.(dtos.DeviceCreateDto), entity.(*domain.Device))
case dtos.DeviceUpdateDto:
MapDeviceUpdateDtoToEntity(dto.(dtos.DeviceUpdateDto), entity.(*domain.Device))
//DeviceNetworkInterface
case dtos.DeviceNetworkInterfaceCreateDto:
MapDeviceNetworkInterfaceCreateDtoToEntity(dto.(dtos.DeviceNetworkInterfaceCreateDto), entity.(*domain.DeviceNetworkInterface))
case dtos.DeviceNetworkInterfaceUpdateDto:
MapDeviceNetworkInterfaceUpdateDtoToEntity(dto.(dtos.DeviceNetworkInterfaceUpdateDto), entity.(*domain.DeviceNetworkInterface))
default:
return errors.Internal.Newf("can't find route for map dto %+v to entity %+v", dto, entity)
}
Expand Down Expand Up @@ -115,7 +125,12 @@ func MapEntityToDto(entity interface{}, dto interface{}) error {
//DHCP4Lease
case domain.DHCP4Lease:
MapDHCP4LeaseToDto(entity.(domain.DHCP4Lease), dto.(*dtos.DHCP4LeaseDto))

//Device
case domain.Device:
MapDeviceToDto(entity.(domain.Device), dto.(*dtos.DeviceDto))
//DeviceNetworkInterface
case domain.DeviceNetworkInterface:
MapDeviceNetworkInterfaceToDto(entity.(domain.DeviceNetworkInterface), dto.(*dtos.DeviceNetworkInterfaceDto))
default:
return errors.Internal.Newf("can't find route for map entity %+v to dto %+v", dto, entity)
}
Expand Down
24 changes: 24 additions & 0 deletions src/app/providers/DevicePowerDriverProvider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package providers

import (
"rol/app/drivers"
"rol/app/interfaces"
"rol/domain"
)

type DevicePowerDriverProvider struct {
poeDriver *drivers.DevicePOEPowerDriver
}

func NewDevicePowerDriverProvider(poeDriver *drivers.DevicePOEPowerDriver) *DevicePowerDriverProvider {
return &DevicePowerDriverProvider{
poeDriver: poeDriver,
}
}

func (p *DevicePowerDriverProvider) GetPowerManagerDriver(device domain.Device) (interfaces.IDevicePowerDriver, error) {
if device.PowerControlBus == "POE" {
return p.poeDriver, nil
}
return nil, nil
}
105 changes: 105 additions & 0 deletions src/app/services/DeviceService.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Package services stores business logic for each entity
package services

import (
"context"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"rol/app/errors"
"rol/app/interfaces"
"rol/app/providers"
"rol/domain"
"rol/dtos"
)

//DeviceService service structure for manage device entity and sub-entities
type DeviceService struct {
devRepo interfaces.IGenericRepository[uuid.UUID, domain.Device]
devEthRepo interfaces.IGenericRepository[uuid.UUID, domain.DeviceNetworkInterface]
powerStateHistory interfaces.IGenericRepository[uuid.UUID, domain.DevicePowerState]
powerDriverProvider *providers.DevicePowerDriverProvider
log *logrus.Logger
}

func NewDeviceService(
devRepo interfaces.IGenericRepository[uuid.UUID, domain.Device],
devEthRepo interfaces.IGenericRepository[uuid.UUID, domain.DeviceNetworkInterface],
powerStateHistory interfaces.IGenericRepository[uuid.UUID, domain.DevicePowerState],
powerDriverProvider *providers.DevicePowerDriverProvider,
log *logrus.Logger,
) *DeviceService {
return &DeviceService{
devRepo: devRepo,
devEthRepo: devEthRepo,
powerStateHistory: powerStateHistory,
powerDriverProvider: powerDriverProvider,
log: log,
}
}

func (s *DeviceService) CheckDeviceExistence(ctx context.Context, deviceID uuid.UUID) error {
exist, err := s.devRepo.IsExist(ctx, deviceID, nil)
if err != nil {
return errors.Internal.Wrapf(err, "failed to check existence of the device with id: %s", deviceID)
}
if !exist {
return errors.NotFound.Newf("device with id %s not found", deviceID)
}
return nil
}

func (s *DeviceService) GetDevicesList(ctx context.Context, search, orderBy, orderDirection string, page, pageSize int) (dtos.PaginatedItemsDto[dtos.DeviceDto], error) {
devices, err := GetList[dtos.DeviceDto](ctx, s.devRepo, search, orderBy, orderDirection, page, pageSize)
if err != nil {
return devices, errors.Internal.Wrap(err, "failed to get tftp server configs list")
}
for i, device := range devices.Items {
(&devices.Items[i]).PowerState = s.getLastPowerStateString(ctx, device.ID)
}
return devices, nil
}

func (s *DeviceService) GetDeviceByID(ctx context.Context, id uuid.UUID) (dtos.DeviceDto, error) {
dto, err := GetByID[dtos.DeviceDto](ctx, s.devRepo, id, nil)
if err != nil {
return dto, err
}
state := s.getLastPowerStateString(ctx, id)
dto.PowerState = state
return dto, nil
}

func (s *DeviceService) UpdateDevice(ctx context.Context, id uuid.UUID, updateDto dtos.DeviceUpdateDto) (dtos.DeviceDto, error) {
dto, err := Update[dtos.DeviceDto](ctx, s.devRepo, updateDto, id, nil)
if err != nil {
return dto, err
}
dto.PowerState = s.getLastPowerStateString(ctx, id)
return dto, nil
}

func (s *DeviceService) CreateDevice(ctx context.Context, createDto dtos.DeviceCreateDto) (dtos.DeviceDto, error) {
dto, err := Create[dtos.DeviceDto](ctx, s.devRepo, createDto)
if err != nil {
return dto, err
}
dto.PowerState = s.getLastPowerStateString(ctx, dto.ID)
return dto, nil
}

func (s *DeviceService) DeleteDevice(ctx context.Context, id uuid.UUID) error {
err := s.CheckDeviceExistence(ctx, id)
if err != nil {
return err
}
queryBuilder := s.getDeviceIDQueryBuilder(ctx, id)
err = s.devEthRepo.DeleteAll(ctx, queryBuilder)
if err != nil {
return errors.Internal.Wrapf(err, "delete network interfaces failed for device %s", id.String())
}
err = s.powerStateHistory.DeleteAll(ctx, queryBuilder)
if err != nil {
return errors.Internal.Wrapf(err, "delete power history failed for device %s", id.String())
}
return s.devRepo.Delete(ctx, id)
}
Loading