diff --git a/client/node.go b/client/node.go index 455f448fb..949aa3692 100644 --- a/client/node.go +++ b/client/node.go @@ -259,9 +259,82 @@ func (n *NodeClient) NetworkListInterfaces(ctx context.Context) (result map[stri return } +// AdminRebootNode stops all the running services and reboots the node +func (n *NodeClient) AdminRebootNode(ctx context.Context) error { + const cmd = "zos.admin.reboot" + + return n.bus.Call(ctx, n.nodeTwin, cmd, nil, nil) +} + +// AdminRestartService restarts a zinit service +func (n *NodeClient) AdminRestartService(ctx context.Context, service string) error { + const cmd = "zos.admin.restart" + + return n.bus.Call(ctx, n.nodeTwin, cmd, service, nil) +} + +// AdminRestartAll restarts all zinit services +func (n *NodeClient) AdminRestartAll(ctx context.Context) error { + const cmd = "zos.admin.restart_all" + + return n.bus.Call(ctx, n.nodeTwin, cmd, nil, nil) +} + +// AdminShowLogs returns l lines of zinit logs +func (n *NodeClient) AdminShowLogs(ctx context.Context, l int) (logs []byte, err error) { + const cmd = "zos.admin.show_logs" + + err = n.bus.Call(ctx, n.nodeTwin, cmd, l, &logs) + return +} + +// AdminShowResolve return the content of /etc/resolv.conf +func (n *NodeClient) AdminShowResolve(ctx context.Context) (res []byte, err error) { + const cmd = "zos.admin.show_resolve" + + err = n.bus.Call(ctx, n.nodeTwin, cmd, nil, &res) + return +} + +// AdminShowOpenConnections return information about all open connections in the node +func (n *NodeClient) AdminShowOpenConnections(ctx context.Context) (res []byte, err error) { + const cmd = "zos.admin.show_open_connections" + + err = n.bus.Call(ctx, n.nodeTwin, cmd, nil, &res) + return +} + +// AdminStopWorkload stops a workload +func (n *NodeClient) AdminStopWorkload(ctx context.Context, twinID uint32, wlID uint64) error { + const cmd = "zos.admin.stop_workload" + args := struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + }{ + TwinID: twinID, + WorkloadID: wlID, + } + + return n.bus.Call(ctx, n.nodeTwin, cmd, args, nil) +} + +// AdminResumeWorkload stops a workload +func (n *NodeClient) AdminResumeWorkload(ctx context.Context, twinID uint32, wlID uint64) error { + const cmd = "zos.admin.resume_workload" + args := struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + }{ + TwinID: twinID, + WorkloadID: wlID, + } + + return n.bus.Call(ctx, n.nodeTwin, cmd, args, nil) +} + // NetworkListAllInterfaces return all physical devices on a node func (n *NodeClient) NetworkListAllInterfaces(ctx context.Context) (result map[string]Interface, err error) { - const cmd = "zos.network.admin.interfaces" + const cmd = "zos.admin.interfaces" err = n.bus.Call(ctx, n.nodeTwin, cmd, nil, &result) @@ -271,14 +344,14 @@ func (n *NodeClient) NetworkListAllInterfaces(ctx context.Context) (result map[s // NetworkSetPublicExitDevice select which physical interface to use as an exit device // setting `iface` to `zos` will then make node run in a single nic setup. func (n *NodeClient) NetworkSetPublicExitDevice(ctx context.Context, iface string) error { - const cmd = "zos.network.admin.set_public_nic" + const cmd = "zos.admin.set_public_nic" return n.bus.Call(ctx, n.nodeTwin, cmd, iface, nil) } // NetworkGetPublicExitDevice gets the current dual nic setup of the node. func (n *NodeClient) NetworkGetPublicExitDevice(ctx context.Context) (exit ExitDevice, err error) { - const cmd = "zos.network.admin.get_public_nic" + const cmd = "zos.admin.get_public_nic" err = n.bus.Call(ctx, n.nodeTwin, cmd, nil, &exit) return diff --git a/cmds/modules/api_gateway/main.go b/cmds/modules/api_gateway/main.go index b0f1f8cc2..55fc3a6c8 100644 --- a/cmds/modules/api_gateway/main.go +++ b/cmds/modules/api_gateway/main.go @@ -68,7 +68,6 @@ func action(cli *cli.Context) error { return fmt.Errorf("failed to create substrate manager: %w", err) } - router := peer.NewRouter() gw, err := substrategw.NewSubstrateGateway(manager, id) if err != nil { return fmt.Errorf("failed to create api gateway: %w", err) @@ -96,6 +95,8 @@ func action(cli *cli.Context) error { if err != nil { return fmt.Errorf("failed to create zos api: %w", err) } + + router := peer.NewRouter() api.SetupRoutes(router) pair, err := id.KeyPair() @@ -105,7 +106,7 @@ func action(cli *cli.Context) error { bo := backoff.NewExponentialBackOff() bo.MaxElapsedTime = 0 - backoff.Retry(func() error { + if err = backoff.Retry(func() error { _, err = peer.NewPeer( ctx, hex.EncodeToString(pair.Seed()), @@ -117,9 +118,10 @@ func action(cli *cli.Context) error { if err != nil { return fmt.Errorf("failed to start a new rmb peer: %w", err) } - return nil - }, bo) + }, bo); err != nil { + return err + } log.Info(). Str("broker", msgBrokerCon). diff --git a/cmds/modules/provisiond/events.go b/cmds/modules/provisiond/events.go index c14d23b05..797f96205 100644 --- a/cmds/modules/provisiond/events.go +++ b/cmds/modules/provisiond/events.go @@ -107,7 +107,7 @@ func (r *ContractEventHandler) sync(ctx context.Context) error { action = r.engine.Pause } - if err := action(ctx, dl.TwinID, dl.ContractID); err != nil { + if err := action(dl.TwinID, dl.ContractID); err != nil { log.Error().Err(err).Msg("failed to change contract state") } } @@ -176,7 +176,7 @@ func (r *ContractEventHandler) Run(ctx context.Context) error { action = r.engine.Pause } - if err := action(ctx, event.TwinId, event.Contract); err != nil { + if err := action(event.TwinId, event.Contract); err != nil { log.Error().Err(err). Uint32("twin", event.TwinId). Uint64("contract", event.Contract). diff --git a/docs/manual/api.md b/docs/manual/api.md index 250eb00f2..6baf7cc22 100644 --- a/docs/manual/api.md +++ b/docs/manual/api.md @@ -84,7 +84,8 @@ so `used = user_used + system`, while `system` is only the amount of resourced r | `zos.storage.pools` | - |`[]Pool`| List all node pools with their types, size and used space -where + +Where ```json Pool { @@ -151,13 +152,95 @@ it means it can act like an access node to user private networks ## Admin -The next set of commands are ONLY possible to be called by the `farmer` only. +The next set of commands are ONLY possible to be called by the `farmer` owning the node. + +### Reboot Node + +| command |body| return| +|---|---|---| +| `zos.admin.reboot` | - | - | + +Stops all services then reboots the node + +### Restart Service + +| command |body| return| +|---|---|---| +| `zos.admin.restart` | string | - | + +Restarts a service running on the node + +### Restart All Services + +| command |body| return| +|---|---|---| +| `zos.admin.restart_all` | - | - | + +Restarts all zinit services running on the node + +### Show Logs + +| command |body| return| +|---|---|---| +| `zos.admin.show_logs` | int | []byte | + +Shows a number of lines of zinit logs + +### Show Resolve + +| command |body| return| +|---|---|---| +| `zos.admin.show_resolve` | - | []byte | + +Shows the content of /etc/resolv.conf + +### Show Open Connections + +| command |body| return| +|---|---|---| +| `zos.admin.show_open_connections` | - | []byte | + +Shows information about all open connections in the node + +### Stop Workload + +| command |body| return| +|---|---|---| +| `zos.admin.Stop` | `Args` | - | + +Where + +```json +Args { + "twin_id": "uint32", + "workload_id": "uint64", +} +``` + +Stops a workload + +### Resume Workload + +| command |body| return| +|---|---|---| +| `zos.admin.resume` | `Args` | - | + +Where + +```json +Args { + "twin_id": "uint32", + "workload_id": "uint64", +} +``` + +Resumes a stopped workload ### List Physical Interfaces | command |body| return| |---|---|---| -| `zos.network.admin.interfaces` | - |`map[string]Interface` | +| `zos.admin.interfaces` | - |`map[string]Interface` | Where @@ -175,7 +258,7 @@ Those interfaces then can be used as an input to `set_public_nic` | command |body| return| |---|---|---| -| `zos.network.admin.get_public_nic` | - |`ExitDevice` | +| `zos.admin.get_public_nic` | - |`ExitDevice` | Where @@ -193,7 +276,7 @@ returns the interface used by public traffic (for user workloads) | command |body| return| |---|---|---| -| `zos.network.admin.set_public_nic` | `name` |- | +| `zos.admin.set_public_nic` | `name` |- | name must be one of (free) names returned by `zos.network.admin.interfaces` @@ -223,7 +306,6 @@ name must be one of (free) names returned by `zos.network.admin.interfaces` |---|---|---| | `zos.system.node_features_get` | - |`[]NodeFeature` | - Where ```json diff --git a/pkg/primitives/statistics.go b/pkg/primitives/statistics.go index 41aa534bd..441d9c2cd 100644 --- a/pkg/primitives/statistics.go +++ b/pkg/primitives/statistics.go @@ -4,6 +4,9 @@ import ( "context" "encoding/json" "fmt" + "os/exec" + "strconv" + "strings" "time" "github.com/pkg/errors" @@ -31,9 +34,7 @@ func GetCapacity(ctx context.Context) gridtypes.Capacity { return val.(gridtypes.Capacity) } -var ( - _ provision.Provisioner = (*Statistics)(nil) -) +var _ provision.Provisioner = (*Statistics)(nil) type Reserved func() (gridtypes.Capacity, error) @@ -146,7 +147,6 @@ func (s *Statistics) hasEnoughCapacity(wl *gridtypes.WorkloadWithID) (gridtypes. id, _ := gridtypes.NewWorkloadID(dl_.TwinID, dl_.ContractID, wl_.Name) return id == wl.ID }) - if err != nil { return used, errors.Wrap(err, "failed to get available memory") } @@ -155,7 +155,7 @@ func (s *Statistics) hasEnoughCapacity(wl *gridtypes.WorkloadWithID) (gridtypes. return used, fmt.Errorf("cannot fulfil required memory size %d bytes out of usable %d bytes", required.MRU, usable) } - //check other resources as well? + // check other resources as well? return used, nil } @@ -235,6 +235,19 @@ func (s *statsStream) Total() gridtypes.Capacity { return s.stats.Total() } +func (s *statsStream) OpenConnections() ([]byte, error) { + return exec.Command("ss", "-ptn", "state", "established").Output() +} + +func (s *statsStream) openConnectionsCount() (int, error) { + cmd := exec.Command("/bin/sh", "-c", "ss -ptn state established | wc -l") + out, err := cmd.Output() + if err != nil { + return 0, err + } + return strconv.Atoi(strings.TrimSpace(string(out))) +} + func (s *statsStream) Workloads() (int, error) { capacity, err := s.stats.storage.Capacity() if err != nil { @@ -253,10 +266,17 @@ func (s *statsStream) GetCounters() (pkg.Counters, error) { if err != nil { return pkg.Counters{}, err } + + connCount, err := s.openConnectionsCount() + if err != nil { + return pkg.Counters{}, err + } + return pkg.Counters{ - Total: s.stats.Total(), - Used: activeCounters.cap, - System: reserved, + Total: s.stats.Total(), + Used: activeCounters.cap, + System: reserved, + OpenConnecions: connCount, Users: pkg.UsersCounters{ Deployments: activeCounters.deployments, Workloads: activeCounters.workloads, @@ -298,10 +318,6 @@ func (s *statsStream) ListGPUs() ([]pkg.GPUInfo, error) { return nil, errors.Wrap(err, "failed to list available devices") } - if err != nil { - return nil, errors.Wrap(err, "failed to list active deployments") - } - used, err := usedGpus() if err != nil { return nil, errors.Wrap(err, "failed to list used gpus") diff --git a/pkg/provision.go b/pkg/provision.go index c0d2ee898..6fae7d98d 100644 --- a/pkg/provision.go +++ b/pkg/provision.go @@ -20,6 +20,8 @@ type Provision interface { Changes(twin uint32, contractID uint64) ([]gridtypes.Workload, error) ListPublicIPs() ([]string, error) ListPrivateIPs(twin uint32, network gridtypes.Name) ([]string, error) + Pause(twin uint32, id uint64) error + Resume(twin uint32, id uint64) error } type Statistics interface { @@ -29,6 +31,7 @@ type Statistics interface { Workloads() (int, error) GetCounters() (Counters, error) ListGPUs() ([]GPUInfo, error) + OpenConnections() ([]byte, error) } type Counters struct { @@ -40,6 +43,8 @@ type Counters struct { System gridtypes.Capacity `json:"system"` // Users statistics by zos Users UsersCounters `json:"users"` + // OpenConnecions number of open connections in the node + OpenConnecions int `json:"open_connections"` } // UsersCounters the expected counters for deployments and workloads diff --git a/pkg/provision/engine.go b/pkg/provision/engine.go index 9842b5307..ca1eb0a6b 100644 --- a/pkg/provision/engine.go +++ b/pkg/provision/engine.go @@ -361,7 +361,7 @@ func (e *NativeEngine) Provision(ctx context.Context, deployment gridtypes.Deplo } // Pause deployment -func (e *NativeEngine) Pause(ctx context.Context, twin uint32, id uint64) error { +func (e *NativeEngine) Pause(twin uint32, id uint64) error { deployment, err := e.storage.Get(twin, id) if err != nil { return err @@ -381,7 +381,7 @@ func (e *NativeEngine) Pause(ctx context.Context, twin uint32, id uint64) error } // Resume deployment -func (e *NativeEngine) Resume(ctx context.Context, twin uint32, id uint64) error { +func (e *NativeEngine) Resume(twin uint32, id uint64) error { deployment, err := e.storage.Get(twin, id) if err != nil { return err diff --git a/pkg/provision/interface.go b/pkg/provision/interface.go index 9a1a0e1c6..7fa228dc5 100644 --- a/pkg/provision/interface.go +++ b/pkg/provision/interface.go @@ -19,8 +19,8 @@ type Engine interface { // and will be processes later Provision(ctx context.Context, wl gridtypes.Deployment) error Deprovision(ctx context.Context, twin uint32, id uint64, reason string) error - Pause(ctx context.Context, twin uint32, id uint64) error - Resume(ctx context.Context, twin uint32, id uint64) error + Pause(twin uint32, id uint64) error + Resume(twin uint32, id uint64) error Update(ctx context.Context, update gridtypes.Deployment) error Storage() Storage Twins() Twins @@ -61,7 +61,7 @@ var ( // ErrDeploymentConflict returned if deployment cannot be stored because // it conflicts with another deployment ErrDeploymentConflict = fmt.Errorf("conflict") - //ErrDeploymentNotExists returned if object not exists + // ErrDeploymentNotExists returned if object not exists ErrDeploymentNotExists = fmt.Errorf("deployment does not exist") // ErrWorkloadNotExist returned by storage if workload does not exist ErrWorkloadNotExist = fmt.Errorf("workload does not exist") @@ -78,10 +78,12 @@ var ( ) // Field interface -type Field interface{} -type VersionField struct { - Version uint32 -} +type ( + Field interface{} + VersionField struct { + Version uint32 + } +) type DescriptionField struct { Description string diff --git a/pkg/stubs/api_gateway_stub.go b/pkg/stubs/api_gateway_stub.go index 498e2e536..97ebbb831 100644 --- a/pkg/stubs/api_gateway_stub.go +++ b/pkg/stubs/api_gateway_stub.go @@ -6,12 +6,11 @@ package stubs import ( "context" - "time" - types "github.com/centrifuge/go-substrate-rpc-client/v4/types" tfchainclientgo "github.com/threefoldtech/tfchain/clients/tfchain-client-go" zbus "github.com/threefoldtech/zbus" pkg "github.com/threefoldtech/zos/pkg" + "time" ) type SubstrateGatewayStub struct { @@ -31,25 +30,6 @@ func NewSubstrateGatewayStub(client zbus.Client) *SubstrateGatewayStub { } } -func (s *SubstrateGatewayStub) GetZosVersion(ctx context.Context) (ret0 string, ret1 error) { - args := []interface{}{} - result, err := s.client.RequestContext(ctx, s.module, s.object, "GetZosVersion", args...) - if err != nil { - panic(err) - } - - result.PanicOnError() - ret1 = result.CallError() - loader := zbus.Loader{ - &ret0, - } - - if err := result.Unmarshal(&loader); err != nil { - panic(err) - } - return -} - func (s *SubstrateGatewayStub) CreateNode(ctx context.Context, arg0 tfchainclientgo.Node) (ret0 uint32, ret1 error) { args := []interface{}{arg0} result, err := s.client.RequestContext(ctx, s.module, s.object, "CreateNode", args...) @@ -305,6 +285,23 @@ func (s *SubstrateGatewayStub) GetTwinByPubKey(ctx context.Context, arg0 []uint8 return } +func (s *SubstrateGatewayStub) GetZosVersion(ctx context.Context) (ret0 string, ret1 error) { + args := []interface{}{} + result, err := s.client.RequestContext(ctx, s.module, s.object, "GetZosVersion", args...) + if err != nil { + panic(err) + } + result.PanicOnError() + ret1 = result.CallError() + loader := zbus.Loader{ + &ret0, + } + if err := result.Unmarshal(&loader); err != nil { + panic(err) + } + return +} + func (s *SubstrateGatewayStub) Report(ctx context.Context, arg0 []tfchainclientgo.NruConsumption) (ret0 types.Hash, ret1 error) { args := []interface{}{arg0} result, err := s.client.RequestContext(ctx, s.module, s.object, "Report", args...) diff --git a/pkg/stubs/provision_stub.go b/pkg/stubs/provision_stub.go index 077bdf6a8..314def388 100644 --- a/pkg/stubs/provision_stub.go +++ b/pkg/stubs/provision_stub.go @@ -159,3 +159,33 @@ func (s *ProvisionStub) ListPublicIPs(ctx context.Context) (ret0 []string, ret1 } return } + +func (s *ProvisionStub) Pause(ctx context.Context, arg0 uint32, arg1 uint64) (ret0 error) { + args := []interface{}{arg0, arg1} + result, err := s.client.RequestContext(ctx, s.module, s.object, "Pause", args...) + if err != nil { + panic(err) + } + result.PanicOnError() + ret0 = result.CallError() + loader := zbus.Loader{} + if err := result.Unmarshal(&loader); err != nil { + panic(err) + } + return +} + +func (s *ProvisionStub) Resume(ctx context.Context, arg0 uint32, arg1 uint64) (ret0 error) { + args := []interface{}{arg0, arg1} + result, err := s.client.RequestContext(ctx, s.module, s.object, "Resume", args...) + if err != nil { + panic(err) + } + result.PanicOnError() + ret0 = result.CallError() + loader := zbus.Loader{} + if err := result.Unmarshal(&loader); err != nil { + panic(err) + } + return +} diff --git a/pkg/stubs/statistics_stub.go b/pkg/stubs/statistics_stub.go index cc7c51dfb..7353cb2a6 100644 --- a/pkg/stubs/statistics_stub.go +++ b/pkg/stubs/statistics_stub.go @@ -79,6 +79,23 @@ func (s *StatisticsStub) ListGPUs(ctx context.Context) (ret0 []pkg.GPUInfo, ret1 return } +func (s *StatisticsStub) OpenConnections(ctx context.Context) (ret0 []uint8, ret1 error) { + args := []interface{}{} + result, err := s.client.RequestContext(ctx, s.module, s.object, "OpenConnections", args...) + if err != nil { + panic(err) + } + result.PanicOnError() + ret1 = result.CallError() + loader := zbus.Loader{ + &ret0, + } + if err := result.Unmarshal(&loader); err != nil { + panic(err) + } + return +} + func (s *StatisticsStub) ReservedStream(ctx context.Context) (<-chan gridtypes.Capacity, error) { ch := make(chan gridtypes.Capacity, 1) recv, err := s.client.Stream(ctx, s.module, s.object, "ReservedStream") diff --git a/pkg/zinit/commands.go b/pkg/zinit/commands.go index 6efd3847c..20218a323 100644 --- a/pkg/zinit/commands.go +++ b/pkg/zinit/commands.go @@ -45,9 +45,9 @@ const ( ServiceStateSuccess = "success" // ServiceStateError is return when we a service exit with an error (exit code != 0) ServiceStateError = "error" - //ServiceStateFailure is set of zinit can not spawn a service in the first place - //due to a missing executable for example. Unlike `error` which is returned if the - //service itself exits with an error. + // ServiceStateFailure is set of zinit can not spawn a service in the first place + // due to a missing executable for example. Unlike `error` which is returned if the + // service itself exits with an error. ServiceStateFailure = "failure" ) @@ -204,7 +204,34 @@ type ServiceStatus struct { func (c *Client) List() (out map[string]ServiceState, err error) { err = c.cmd("list", &out) return +} + +// List returns all the service monitored and their status +func (c *Client) Log(n int) (out []byte, err error) { + cmd1 := exec.Command("zinit", "log", "-s") + cmd2 := exec.Command("tail", "-n", fmt.Sprint(n)) + + cmd2.Stdin, err = cmd1.StdoutPipe() + if err != nil { + return + } + + err = cmd1.Start() + if err != nil { + return + } + output, err := cmd2.Output() + if err != nil { + return + } + + err = cmd1.Wait() + if err != nil { + return + } + + return output, err } // Status returns the status of a service @@ -310,6 +337,11 @@ func (c *Client) Stop(service string) error { return c.cmd(fmt.Sprintf("stop %s", service), nil) } +// Restart restarts a service. +func (c *Client) Restart(service string) error { + return c.cmd(fmt.Sprintf("restart %s", service), nil) +} + // StartWait starts a service and wait until its running, or until the timeout // (seconds) pass. If timedout, the method returns an error if the service is not running // timout of 0 means no wait. (similar to Stop) diff --git a/pkg/zos_api/admin.go b/pkg/zos_api/admin.go index 4b53237e6..c1eba978d 100644 --- a/pkg/zos_api/admin.go +++ b/pkg/zos_api/admin.go @@ -4,8 +4,94 @@ import ( "context" "encoding/json" "fmt" + "os" + "path/filepath" + + "github.com/rs/zerolog/log" + "github.com/threefoldtech/zos/pkg/zinit" ) +func (g *ZosAPI) adminRebootHandler(ctx context.Context, payload []byte) (interface{}, error) { + zinit := zinit.Default() + + return nil, zinit.Reboot() +} + +func (g *ZosAPI) adminRestartServiceHandler(ctx context.Context, payload []byte) (interface{}, error) { + var service string + if err := json.Unmarshal(payload, &service); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting string: %w", err) + } + + zinit := zinit.Default() + + return nil, zinit.Restart(service) +} + +func (g *ZosAPI) adminRestartAllHandler(ctx context.Context, payload []byte) (interface{}, error) { + zinit := zinit.Default() + + services, err := zinit.List() + if err != nil { + return nil, fmt.Errorf("failed to list node services, expecting string: %w", err) + } + + for service := range services { + log.Debug().Str("service", service).Send() + if err := zinit.Restart(service); err != nil { + return nil, fmt.Errorf("failed to reboot service %s, expecting string: %w", service, err) + } + } + + return nil, nil +} + +func (g *ZosAPI) adminShowLogsHandler(ctx context.Context, payload []byte) (interface{}, error) { + var n int + if err := json.Unmarshal(payload, &n); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting string: %w", err) + } + + zinit := zinit.Default() + + return zinit.Log(n) +} + +func (g *ZosAPI) adminShowResolveHandler(ctx context.Context, payload []byte) (interface{}, error) { + path := filepath.Join("/etc", "resolv.conf") + return os.ReadFile(path) +} + +func (g *ZosAPI) adminShowOpenConnectionsHandler(ctx context.Context, payload []byte) (interface{}, error) { + return g.statisticsStub.OpenConnections(ctx) +} + +func (g *ZosAPI) adminStopWorkloadHandler(ctx context.Context, payload []byte) (interface{}, error) { + var args struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + } + + if err := json.Unmarshal(payload, &args); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting twin id and workload id: %w", err) + } + + return nil, g.provisionStub.Pause(ctx, args.TwinID, args.WorkloadID) +} + +func (g *ZosAPI) adminResumeWorkloadHandler(ctx context.Context, payload []byte) (interface{}, error) { + var args struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + } + + if err := json.Unmarshal(payload, &args); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting twin id and workload id: %w", err) + } + + return nil, g.provisionStub.Resume(ctx, args.TwinID, args.WorkloadID) +} + func (g *ZosAPI) adminInterfacesHandler(ctx context.Context, payload []byte) (interface{}, error) { // list all interfaces on node type Interface struct { diff --git a/pkg/zos_api/routes.go b/pkg/zos_api/routes.go index 8006f23b7..ef1da4690 100644 --- a/pkg/zos_api/routes.go +++ b/pkg/zos_api/routes.go @@ -45,6 +45,17 @@ func (g *ZosAPI) SetupRoutes(router *peer.Router) { admin := root.SubRoute("admin") admin.Use(g.authorized) + admin.WithHandler("reboot", g.adminRebootHandler) + admin.WithHandler("restart", g.adminRestartServiceHandler) + admin.WithHandler("restart_all", g.adminRestartAllHandler) + + admin.WithHandler("show_logs", g.adminShowLogsHandler) + admin.WithHandler("show_resolve", g.adminShowResolveHandler) + admin.WithHandler("show_open_connections", g.adminShowOpenConnectionsHandler) + + admin.WithHandler("stop_workload", g.adminStopWorkloadHandler) + admin.WithHandler("resume_workload", g.adminResumeWorkloadHandler) + admin.WithHandler("interfaces", g.adminInterfacesHandler) admin.WithHandler("set_public_nic", g.adminSetPublicNICHandler) admin.WithHandler("get_public_nic", g.adminGetPublicNICHandler)