Files
vxcommon/loader/module.go
T
2021-03-22 12:45:34 +03:00

221 lines
6.1 KiB
Go

package loader
import (
"encoding/json"
"errors"
"sync"
"github.com/vxcontrol/luar"
"github.com/vxcontrol/vxcommon/agent"
"github.com/vxcontrol/vxcommon/lua"
"github.com/vxcontrol/vxcommon/vxproto"
)
type deferredModuleCallback func() bool
// ModuleState is struct for contains information about module
type ModuleState struct {
name string
item *ModuleItem
status agent.ModuleStatus_Status
wg sync.WaitGroup
luaModule *lua.Module
luaState *lua.State
cbStart deferredModuleCallback
cbStop deferredModuleCallback
}
// NewState is function which constructed ModuleState object
func NewState(mc *ModuleConfig, mi *ModuleItem, p vxproto.IVXProto) (*ModuleState, error) {
ms := &ModuleState{
name: mc.Name,
item: mi,
status: agent.ModuleStatus_UNKNOWN,
}
socket := p.NewModule(ms.name, mc.AgentID)
if socket == nil {
return nil, errors.New("module socket for " + ms.name + " not initialized")
}
ms.cbStart = func() bool {
agents := make(map[string]*vxproto.AgentInfo)
for dst, info := range p.GetAgentList() {
if mc.AgentID == "" || info.ID == mc.AgentID {
agents[dst] = info
}
}
ms.luaModule.SetAgents(agents)
if !p.AddModule(socket) {
socket.Close()
return false
}
return true
}
ms.cbStop = func() bool {
ms.luaModule.SetAgents(make(map[string]*vxproto.AgentInfo))
return p.DelModule(socket)
}
var err error
if ms.luaState, err = lua.NewState(mi.files); err != nil {
return nil, errors.New("error with creating new state: " + err.Error())
}
if ms.luaModule, err = lua.NewModule(mi.args, ms.luaState, socket); err != nil {
return nil, errors.New("error with creating new module: " + err.Error())
}
// TODO: here need to register API for communication to logging subsystem
luar.Register(ms.luaState.L, "__config", luar.Map{
"get_config_schema": mc.GetConfigSchema,
"get_default_config": mc.GetDefaultConfig,
"get_current_config": mc.GetCurrentConfig,
"get_event_data_schema": mc.GetEventDataSchema,
"get_event_config_schema": mc.GetEventConfigSchema,
"get_default_event_config": mc.GetDefaultEventConfig,
"get_current_event_config": mc.GetCurrentEventConfig,
"set_current_config": func(c string) bool {
if mc.SetCurrentConfig(c) {
ms.wg.Add(1)
go func() {
defer ms.wg.Done()
ms.luaModule.ControlMsg("update_config", c)
}()
return true
}
return false
},
"get_module_info": func() string {
if dinfo, err := json.Marshal(mc); err == nil {
return string(dinfo)
}
return ""
},
"ctx": luar.Map{
"agent_id": mc.AgentID,
"os": mc.OS,
"name": mc.Name,
"version": mc.Version,
"events": mc.Events,
"last_update": mc.LastUpdate,
},
})
ms.status = agent.ModuleStatus_LOADED
return ms, nil
}
// Start is function for running server module
func (ms *ModuleState) Start() error {
switch ms.status {
case agent.ModuleStatus_UNKNOWN:
return errors.New("module " + ms.name + " can't loaded")
case agent.ModuleStatus_RUNNING:
return errors.New("module " + ms.name + " already running")
case agent.ModuleStatus_FREED:
return errors.New("module " + ms.name + " already closed")
case agent.ModuleStatus_LOADED:
fallthrough
case agent.ModuleStatus_STOPPED:
if !ms.cbStart() {
return errors.New("module socket for " + ms.name + " not registered")
}
ms.wg.Add(1)
ms.status = agent.ModuleStatus_RUNNING
go func(ms *ModuleState) {
defer ms.wg.Done()
ms.luaModule.Start()
ms.status = agent.ModuleStatus_STOPPED
}(ms)
default:
return errors.New("undefined module " + ms.name + " status")
}
return nil
}
// Stop is function for stopping server module
func (ms *ModuleState) Stop() error {
switch ms.status {
case agent.ModuleStatus_UNKNOWN:
return errors.New("module " + ms.name + " can't loaded")
case agent.ModuleStatus_LOADED:
ms.status = agent.ModuleStatus_STOPPED
case agent.ModuleStatus_STOPPED:
return errors.New("module " + ms.name + " already stopped")
case agent.ModuleStatus_FREED:
return errors.New("module " + ms.name + " already closed")
case agent.ModuleStatus_RUNNING:
ms.luaModule.Stop()
if !ms.cbStop() {
return errors.New("can't stop module socket for " + ms.name)
}
ms.status = agent.ModuleStatus_STOPPED
default:
return errors.New("undefined module " + ms.name + " status")
}
return nil
}
// Close is function for release module object
func (ms *ModuleState) Close() error {
switch ms.status {
case agent.ModuleStatus_UNKNOWN:
return errors.New("module " + ms.name + " can't loaded")
case agent.ModuleStatus_LOADED:
return errors.New("module " + ms.name + " wasn't running")
case agent.ModuleStatus_FREED:
return errors.New("module " + ms.name + " already closed")
case agent.ModuleStatus_RUNNING:
ms.luaModule.Stop()
if !ms.cbStop() {
return errors.New("can't stop module socket for " + ms.name)
}
ms.status = agent.ModuleStatus_STOPPED
fallthrough
case agent.ModuleStatus_STOPPED:
ms.wg.Wait()
luar.Register(ms.luaState.L, "__config", luar.Map{})
ms.luaModule.Close()
ms.luaState = nil
ms.status = agent.ModuleStatus_FREED
default:
return errors.New("undefined module " + ms.name + " status")
}
return nil
}
// GetName is function that return module name
func (ms *ModuleState) GetName() string {
return ms.name
}
// GetStatus is function that return module status
func (ms *ModuleState) GetStatus() agent.ModuleStatus_Status {
return ms.status
}
// GetResult is function that return module re3sult after close
func (ms *ModuleState) GetResult() string {
return ms.luaModule.GetResult()
}
// GetState is function that return current lua state
func (ms *ModuleState) GetState() *lua.State {
return ms.luaState
}
// GetModule is function that return current lua module object
func (ms *ModuleState) GetModule() *lua.Module {
return ms.luaModule
}
// GetItem is function that return module item (args, files and config)
func (ms *ModuleState) GetItem() *ModuleItem {
return ms.item
}