From 60e9de8d632615f053111249a42a90f310cb100e Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 18 Oct 2022 19:42:14 +0400 Subject: [PATCH] [#1916] control: Check maintenance allowance on Control server In previous implementation turning to maintenance mode using NeoFS CLI required NeoFS API endpoint. This was not convenient from the user perspective. It's worth to move networks settings' check to the server side. Add `force_maintenance` field to `SetNetmapStatusRequest.Body` message of Control API. Add `force` flag to `neofs-cli control set-status` command which sets corresponding field in the requests body if status is `maintenance`. Force flag is ignored for any other status. Signed-off-by: Leonard Lyubich --- CHANGELOG.md | 6 +++ .../modules/control/set_netmap_status.go | 51 +++++++----------- cmd/neofs-node/netmap.go | 30 ++++++++++- docs/maintenance.md | 3 +- pkg/services/control/server/server.go | 10 +++- .../control/server/set_netmap_status.go | 19 ++++++- pkg/services/control/service.go | 5 ++ pkg/services/control/service.pb.go | Bin 110698 -> 111678 bytes pkg/services/control/service.proto | 9 ++++ pkg/services/control/service_neofs.pb.go | Bin 52672 -> 52785 bytes 10 files changed, 94 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82a791228..d2226c877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,18 +9,24 @@ Changelog for NeoFS Node - `tree list` CLI command (#1332) - `TreeService.GetTrees` RPC (#1902) - All trees synchronization on bootstrap (#1902) +- `--force` flag to `neofs-cli control set-status` command (#1916) ### Changed ### Fixed - `writecache.max_object_size` is now correctly handled (#1925) - Correctly handle setting ONLINE netmap status after maintenance (#1922) - Correctly reset shard errors in `ControlService.SetShardMode` RPC (#1931) +- Setting node's network state to `MAINTENANCE` while network settings forbid it (#1916) ### Removed ### Updated - `neo-go` to `v0.99.4` ### Updating from v0.33.0 +Now storage node serves Control API `SetNemapStatus` request with `MAINTENANCE` +status only if the mode is allowed in the network settings. To force starting the local +maintenance on the node, provide `--force` flag to the `neofs-cli control set-status` +command. ## [0.33.0] - 2022-10-17 - Anmado (안마도, 鞍馬島) diff --git a/cmd/neofs-cli/modules/control/set_netmap_status.go b/cmd/neofs-cli/modules/control/set_netmap_status.go index 9acc2a156..b0c5c12c6 100644 --- a/cmd/neofs-cli/modules/control/set_netmap_status.go +++ b/cmd/neofs-cli/modules/control/set_netmap_status.go @@ -4,13 +4,11 @@ import ( "fmt" rawclient "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" - internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags" "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key" "github.com/nspcc-dev/neofs-node/pkg/services/control" "github.com/spf13/cobra" - "github.com/spf13/viper" ) const ( @@ -41,53 +39,40 @@ func initControlSetNetmapStatusCmd() { ) _ = setNetmapStatusCmd.MarkFlagRequired(netmapStatusFlag) + + flags.BoolP(commonflags.ForceFlag, commonflags.ForceFlagShorthand, false, + "Force turning to local maintenance") } func setNetmapStatus(cmd *cobra.Command, _ []string) { pk := key.Get(cmd) + body := new(control.SetNetmapStatusRequest_Body) + force, _ := cmd.Flags().GetBool(commonflags.ForceFlag) - var status control.NetmapStatus + printIgnoreForce := func(st control.NetmapStatus) { + if force { + common.PrintVerbose("Ignore --%s flag for %s state.", commonflags.ForceFlag, st) + } + } switch st, _ := cmd.Flags().GetString(netmapStatusFlag); st { default: common.ExitOnErr(cmd, "", fmt.Errorf("unsupported status %s", st)) case netmapStatusOnline: - status = control.NetmapStatus_ONLINE + body.SetStatus(control.NetmapStatus_ONLINE) + printIgnoreForce(control.NetmapStatus_ONLINE) case netmapStatusOffline: - status = control.NetmapStatus_OFFLINE + body.SetStatus(control.NetmapStatus_OFFLINE) + printIgnoreForce(control.NetmapStatus_OFFLINE) case netmapStatusMaintenance: - status = control.NetmapStatus_MAINTENANCE + body.SetStatus(control.NetmapStatus_MAINTENANCE) - common.PrintVerbose("Reading network settings to check allowance of \"%s\" mode...", st) - - if !viper.IsSet(commonflags.RPC) { - common.ExitOnErr(cmd, "", - fmt.Errorf("flag --%s (-%s) is not set, you must specify it for \"%s\" mode", - commonflags.RPC, - commonflags.RPCShorthand, - st, - ), - ) + if force { + body.SetForceMaintenance() + common.PrintVerbose("Local maintenance will be forced.") } - - cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC) - - var prm internalclient.NetworkInfoPrm - prm.SetClient(cli) - - res, err := internalclient.NetworkInfo(prm) - common.ExitOnErr(cmd, "receive network info: %v", err) - - if !res.NetworkInfo().MaintenanceModeAllowed() { - common.ExitOnErr(cmd, "", fmt.Errorf("\"%s\" mode is not allowed by the network", st)) - } - - common.PrintVerbose("\"%s\" mode is allowed, continue processing...", st) } - body := new(control.SetNetmapStatusRequest_Body) - body.SetStatus(status) - req := new(control.SetNetmapStatusRequest) req.SetBody(body) diff --git a/cmd/neofs-node/netmap.go b/cmd/neofs-node/netmap.go index 0bb668e47..1bd050541 100644 --- a/cmd/neofs-node/netmap.go +++ b/cmd/neofs-node/netmap.go @@ -340,8 +340,7 @@ func (c *cfg) SetNetmapStatus(st control.NetmapStatus) error { default: return fmt.Errorf("unsupported status %v", st) case control.NetmapStatus_MAINTENANCE: - c.startMaintenance() - return c.updateNetMapState((*nmClient.UpdatePeerPrm).SetMaintenance) + return c.setMaintenanceStatus(false) case control.NetmapStatus_ONLINE, control.NetmapStatus_OFFLINE: } @@ -361,6 +360,33 @@ func (c *cfg) SetNetmapStatus(st control.NetmapStatus) error { return c.updateNetMapState(func(*nmClient.UpdatePeerPrm) {}) } +func (c *cfg) ForceMaintenance() error { + return c.setMaintenanceStatus(true) +} + +func (c *cfg) setMaintenanceStatus(force bool) error { + netSettings, err := c.cfgNetmap.wrapper.ReadNetworkConfiguration() + if err != nil { + err = fmt.Errorf("read network settings to check maintenance allowance: %w", err) + } else if !netSettings.MaintenanceModeAllowed { + err = errors.New("maintenance mode is not allowed by the network") + } + + if err == nil || force { + c.startMaintenance() + + if err == nil { + err = c.updateNetMapState((*nmClient.UpdatePeerPrm).SetMaintenance) + } + + if err != nil { + return fmt.Errorf("local maintenance is started, but state is not updated in the network: %w", err) + } + } + + return err +} + // calls UpdatePeerState operation of Netmap contract's client for the local node. // State setter is used to specify node state to switch to. func (c *cfg) updateNetMapState(stateSetter func(*nmClient.UpdatePeerPrm)) error { diff --git a/docs/maintenance.md b/docs/maintenance.md index 07e7806b2..e135638a5 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -32,8 +32,9 @@ $ neofs-adm morph set-config MaintenanceModeAllowed=true|false To switch the node to MM, exec: ```shell -$ neofs-cli control set-status --status maintenance +$ neofs-cli control set-status --status maintenance [--force|-f] ``` +`-f` flag allows to force local maintenance regardless of the network settings. To stop the maintenance, use the same command but with any other supported state. diff --git a/pkg/services/control/server/server.go b/pkg/services/control/server/server.go index 920e9bfd3..5fbc70df4 100644 --- a/pkg/services/control/server/server.go +++ b/pkg/services/control/server/server.go @@ -34,7 +34,15 @@ type HealthChecker interface { // NodeState is an interface of storage node network state. type NodeState interface { - SetNetmapStatus(control.NetmapStatus) error + // SetNetmapStatus switches the storage node to the given network status. + // + // If status is control.NetmapStatus_MAINTENANCE and maintenance is allowed + // in the network settings, the node additionally starts local maintenance. + SetNetmapStatus(st control.NetmapStatus) error + + // ForceMaintenance works like SetNetmapStatus(control.NetmapStatus_MAINTENANCE) + // but starts local maintenance regardless of the network settings. + ForceMaintenance() error } // Option of the Server's constructor. diff --git a/pkg/services/control/server/set_netmap_status.go b/pkg/services/control/server/set_netmap_status.go index ec03bdecd..bfdd1e029 100644 --- a/pkg/services/control/server/set_netmap_status.go +++ b/pkg/services/control/server/set_netmap_status.go @@ -17,8 +17,23 @@ func (s *Server) SetNetmapStatus(ctx context.Context, req *control.SetNetmapStat return nil, status.Error(codes.PermissionDenied, err.Error()) } - // set node status - if err := s.nodeState.SetNetmapStatus(req.GetBody().GetStatus()); err != nil { + var err error + bodyReq := req.GetBody() + st := bodyReq.GetStatus() + force := bodyReq.GetForceMaintenance() + + if force { + if st != control.NetmapStatus_MAINTENANCE { + return nil, status.Errorf(codes.InvalidArgument, + "force_maintenance MUST be set for %s status only", control.NetmapStatus_MAINTENANCE) + } + + err = s.nodeState.ForceMaintenance() + } else { + err = s.nodeState.SetNetmapStatus(st) + } + + if err != nil { return nil, status.Error(codes.Aborted, err.Error()) } diff --git a/pkg/services/control/service.go b/pkg/services/control/service.go index 7de516bcf..1bf273360 100644 --- a/pkg/services/control/service.go +++ b/pkg/services/control/service.go @@ -35,6 +35,11 @@ func (x *SetNetmapStatusRequest_Body) SetStatus(v NetmapStatus) { } } +// SetForceMaintenance sets force_maintenance flag in the message. +func (x *SetNetmapStatusRequest_Body) SetForceMaintenance() { + x.ForceMaintenance = true +} + // SetBody sets body of the set netmap status request . func (x *SetNetmapStatusRequest) SetBody(v *SetNetmapStatusRequest_Body) { if x != nil { diff --git a/pkg/services/control/service.pb.go b/pkg/services/control/service.pb.go index 18322a093b1b07b1c06ab1fa6725e3a088ecf341..c18408ed7c74380caaf3951cdfbd1bc98e8f403c 100644 GIT binary patch delta 4158 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 U?{T3*-c4S>G5_{%`r+7r0TZRgK>z>% diff --git a/pkg/services/control/service.proto b/pkg/services/control/service.proto index a72ee57a5..302602deb 100644 --- a/pkg/services/control/service.proto +++ b/pkg/services/control/service.proto @@ -75,7 +75,16 @@ message SetNetmapStatusRequest { // Set netmap status request body. message Body { // New storage node status in NeoFS network map. + // If status is MAINTENANCE, the node checks whether maintenance is + // allowed in the network settings. In case of prohibition, the request + // is denied. Otherwise, node switches to local maintenance state. To + // force local maintenance, use `force_maintenance` flag. NetmapStatus status = 1; + + // MAINTENANCE status validation skip flag. If set, node starts local + // maintenance regardless of network settings. The flag MUST NOT be + // set for any other status. + bool force_maintenance = 2; } // Body of set netmap status request message. diff --git a/pkg/services/control/service_neofs.pb.go b/pkg/services/control/service_neofs.pb.go index 94b9fcc6b86f3110b3e1c2c66e7d861d7d76a31e..2d891bb8195ad4e850b065ef1ab37cf939895e2a 100644 GIT binary patch delta 105 zcmX>wn|b3L<_%9pC%dGJix}xBROq?o7bU0qCT8Z9q~;~&C2!szHiL0;o;u%TeQDOo wJ>u6VoAd}w*3aUZY`a33A5HOOMk~?D4)Gn6H^_-@b`KL^0%_c=FFkP+03cr`W&i*H delta 19 bcmdluhxx#4<_%9pH;ahXF>Ssg5xW%tR+$KA