diff --git a/controller/controller.go b/controller/controller.go index d9118331755a1a9d3d51391218c1c35347cdfad3..d16fd4bf1d5fdba44f7ddde102a0b6a5dd3b43ff 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -392,7 +392,7 @@ func shutdown() error { } for _, plugin := range plugins { log.Info("Defer: ", plugin.Manifest().Name) - plugin.GetClient().Kill() + plugin.Close() log.Info("Defer - exited: ", plugin.GetClient().Exited()) } coreLock.Unlock() diff --git a/controller/interfaces/plugin/plugin.go b/controller/interfaces/plugin/plugin.go index f61bda8a8ec589486a6452141d4e07727790e2c3..e51743309a2cff64ada5a8d5f9574646ef6281b5 100644 --- a/controller/interfaces/plugin/plugin.go +++ b/controller/interfaces/plugin/plugin.go @@ -46,6 +46,7 @@ type Plugin interface { Ping() error Restart() error Close() + Remove() error shared.DeviceModel } diff --git a/controller/mocks/Plugin.go b/controller/mocks/Plugin.go index 9af1bf4b7fdd6d3d10638d114607774656129649..462e11d8906c655cffdb126f245823dd0dc47bc8 100644 --- a/controller/mocks/Plugin.go +++ b/controller/mocks/Plugin.go @@ -219,6 +219,20 @@ func (_m *Plugin) PruneConfigFalse(value []byte) ([]byte, error) { return r0, r1 } +// Remove provides a mock function with given fields: +func (_m *Plugin) Remove() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + // Restart provides a mock function with given fields: func (_m *Plugin) Restart() error { ret := _m.Called() diff --git a/controller/northbound/server/configurationmanagement.go b/controller/northbound/server/configurationmanagement.go index 7dd74e3cc851eabd66ae09c30166ca7a1dda0811..ebe8436db8457ad8c095a9a759078729a2db8f0d 100644 --- a/controller/northbound/server/configurationmanagement.go +++ b/controller/northbound/server/configurationmanagement.go @@ -337,6 +337,10 @@ func (c ConfigurationManagementServer) createNetworkElements(sdnConfig *loadedSD return err } + if err := c.pluginService.Add(plugin); err != nil { + return err + } + err = c.mneService.UpdateModel(createdNetworkElement.ID(), inputNetworkElement.Model) if err != nil { return err diff --git a/controller/northbound/server/networkElement.go b/controller/northbound/server/networkElement.go index 1528f7514638f0cad0540205ef70fc1044f61e9e..89407828b56b8574ae605da29d5a46d6e2994be0 100644 --- a/controller/northbound/server/networkElement.go +++ b/controller/northbound/server/networkElement.go @@ -707,25 +707,6 @@ func (n *NetworkElementServer) SetMneList(ctx context.Context, request *mnepb.Se func (n *NetworkElementServer) addMne(ctx context.Context, name string, opt *tpb.TransportOption, requestPluginFunc func(uuid.UUID) (plugin.Plugin, error), pluginID uuid.UUID, pndID uuid.UUID, optionalNetworkElementID ...uuid.UUID) (uuid.UUID, error) { var err error - // Note: cSBI not supported currently, so this is commented fow now. - // Might be needed or removed in the future. - // - // switch t := opt.Type; t { - // case spb.Type_TYPE_CONTAINERISED: - // return n.handleCsbiEnrolment(name, opt) - // case spb.Type_TYPE_PLUGIN: - // sbi, err = n.requestPlugin(name, opt) - // if err != nil { - // return uuid.Nil, err - // } - // default: - // var err error - // sbi, err = pnd.southboundService.Get(store.Query{ID: sid}) - // if err != nil { - // return uuid.Nil, err - // } - // } - networkElementID := uuid.Nil if len(optionalNetworkElementID) > 0 { networkElementID = optionalNetworkElementID[0] @@ -742,33 +723,67 @@ func (n *NetworkElementServer) addMne(ctx context.Context, name string, opt *tpb mne, err := nucleus.NewNetworkElement(name, networkElementID, opt, pndID, plugin, conflict.Metadata{ResourceVersion: 0}) if err != nil { + if pluginRmErr := plugin.Remove(); err != nil { + return uuid.Nil, pluginRmErr + } return uuid.Nil, err } if mne.IsTransportValid() { - resp, err := n.getPath(ctx, mne, "/") + err := n.initialNetworkElementRootPathRequest(ctx, mne, plugin) if err != nil { return uuid.Nil, err } - err = mne.ProcessResponse(resp) + err = n.mneService.Add(mne) if err != nil { + if pluginRmErr := plugin.Remove(); err != nil { + return uuid.Nil, pluginRmErr + } return uuid.Nil, err } - err = n.mneService.Add(mne) + err = n.pluginService.Add(plugin) if err != nil { + if pluginRmErr := plugin.Remove(); err != nil { + return uuid.Nil, pluginRmErr + } + if err := n.mneService.Delete(mne); err != nil { + return uuid.Nil, err + } return uuid.Nil, err } n.networkElementWatchter.SubscribeToNetworkElement(mne, config.GetGnmiSubscriptionPaths(), nil) } else { - return uuid.Nil, status.Errorf(codes.InvalidArgument, "invalid transport data provided") + if pluginRmErr := plugin.Remove(); err != nil { + return uuid.Nil, pluginRmErr + } + return uuid.Nil, fmt.Errorf("invalid transport data provided") } return mne.ID(), nil } +func (n *NetworkElementServer) initialNetworkElementRootPathRequest(ctx context.Context, mne networkelement.NetworkElement, plugin plugin.Plugin) error { + resp, err := n.getPath(ctx, mne, "/") + if err != nil { + if pluginRmErr := plugin.Remove(); err != nil { + return pluginRmErr + } + return err + } + + err = mne.ProcessResponse(resp) + if err != nil { + if pluginRmErr := plugin.Remove(); err != nil { + return pluginRmErr + } + return err + } + return nil +} + // SetChangeList sets a list of changes. func (n *NetworkElementServer) SetChangeList(ctx context.Context, request *mnepb.SetChangeListRequest) (*mnepb.SetChangeListResponse, error) { labels := prometheus.Labels{"service": "mne", "rpc": "set"} diff --git a/controller/nucleus/plugin.go b/controller/nucleus/plugin.go index 032c30ed4136f8741ff6a13de47b19f5a4a3275c..b409e6252ea0476fcaff8de5d385f172609d09be 100644 --- a/controller/nucleus/plugin.go +++ b/controller/nucleus/plugin.go @@ -3,6 +3,7 @@ package nucleus import ( "encoding/json" "fmt" + "os" "os/exec" "path/filepath" @@ -30,7 +31,7 @@ func NewPlugin(id uuid.UUID, execPath string) (*Plugin, error) { client := hcplugin.NewClient(&hcplugin.ClientConfig{ HandshakeConfig: shared.Handshake, Plugins: shared.PluginMap, - Cmd: exec.Command("sh", "-c", filepath.Join(execPath, util.PluginExecutableName)), + Cmd: exec.Command(filepath.Join(execPath, util.PluginExecutableName)), AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC}, }) @@ -129,6 +130,15 @@ func (p *Plugin) ReattachConfig() *hcplugin.ReattachConfig { return p.client.ReattachConfig() } +// Remove ensures that the Plugin is killed and the corresponding files are +// removed. +func (p *Plugin) Remove() error { + // stop the running plugins process + p.Close() + // remove the plugins folder + return os.RemoveAll(p.ExecPath()) +} + // State returns the current state of the plugin. // Different states of the plugin can be: // - created @@ -165,6 +175,7 @@ func (p *Plugin) Restart() error { // Close ends the execution of the plugin. func (p *Plugin) Close() { + // end the plugin process p.client.Kill() } diff --git a/controller/nucleus/pluginService.go b/controller/nucleus/pluginService.go index ed7a7ab689cd65e1c7f8ae408de107812a1f454b..3a1319efdc90595c9d3f6e3249d5ff7fd26250b3 100644 --- a/controller/nucleus/pluginService.go +++ b/controller/nucleus/pluginService.go @@ -106,7 +106,7 @@ func (s *PluginService) Delete(pluginToDelete plugin.Plugin) error { } // stop the plugin - pluginToDelete.GetClient().Kill() + pluginToDelete.Close() if err := s.eventService.PublishEvent(PluginEventTopic, event.NewDeleteEvent(pluginToDelete.ID())); err != nil { log.Error(err) @@ -167,10 +167,6 @@ func (s *PluginService) RequestPlugin(requestID uuid.UUID) (plugin.Plugin, error return nil, err } - if err := s.Add(plugin); err != nil { - return nil, err - } - return plugin, nil }