diff --git a/CHANGELOG.md b/CHANGELOG.md index 82a7912280..d2226c8772 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 9acc2a1568..b0c5c12c6d 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 0bb668e47c..1bd0505412 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 07e7806b20..e135638a5a 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 920e9bfd32..5fbc70df40 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 ec03bdecda..bfdd1e0294 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 7de516bcfc..1bf273360a 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 18322a093b..c18408ed7c 100644 Binary files a/pkg/services/control/service.pb.go and b/pkg/services/control/service.pb.go differ diff --git a/pkg/services/control/service.proto b/pkg/services/control/service.proto index a72ee57a5f..302602deb5 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 94b9fcc6b8..2d891bb819 100644 Binary files a/pkg/services/control/service_neofs.pb.go and b/pkg/services/control/service_neofs.pb.go differ