Skip to content

Commit

Permalink
Merge pull request #829 from BajuMcBites/instance-uptime/sb
Browse files Browse the repository at this point in the history
Add instance start timestamp (uptime)
  • Loading branch information
stgraber committed May 4, 2024
2 parents fe31fd5 + 5042c34 commit 974df22
Show file tree
Hide file tree
Showing 21 changed files with 944 additions and 761 deletions.
4 changes: 4 additions & 0 deletions cmd/incus/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,10 @@ func (c *cmdInfo) instanceInfo(d incus.InstanceServer, remote config.Remote, nam
}

if inst.State.Pid != 0 {
if !inst.State.StartedAt.IsZero() {
fmt.Printf(i18n.G("Started: %s")+"\n", inst.State.StartedAt.Local().Format(dateLayout))
}

fmt.Println("\n" + i18n.G("Resources:"))
// Processes
fmt.Printf(" "+i18n.G("Processes: %d")+"\n", inst.State.Processes)
Expand Down
10 changes: 10 additions & 0 deletions cmd/incus/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ Pre-defined column shorthand chars:
S - Number of snapshots
t - Type (persistent or ephemeral)
u - CPU usage (in seconds)
U - Started date
L - Location of the instance (e.g. its cluster member)
f - Base Image Fingerprint (short)
F - Base Image Fingerprint (long)
Expand Down Expand Up @@ -586,6 +587,7 @@ func (c *cmdList) parseColumns(clustered bool) ([]column, bool, error) {
's': {i18n.G("STATE"), c.statusColumnData, false, false},
't': {i18n.G("TYPE"), c.typeColumnData, false, false},
'u': {i18n.G("CPU USAGE"), c.cpuUsageSecondsColumnData, true, false},
'U': {i18n.G("STARTED AT"), c.startedColumnData, true, false},
}

// Add project column if --all-projects flag specified and
Expand Down Expand Up @@ -927,6 +929,14 @@ func (c *cmdList) CreatedColumnData(cInfo api.InstanceFull) string {
return ""
}

func (c *cmdList) startedColumnData(cInfo api.InstanceFull) string {
if cInfo.State != nil && !cInfo.State.StartedAt.IsZero() {
return cInfo.State.StartedAt.Local().Format(dateLayout)
}

return ""
}

func (c *cmdList) LastUsedColumnData(cInfo api.InstanceFull) string {
if !cInfo.LastUsedAt.IsZero() {
return cInfo.LastUsedAt.Local().Format(dateLayout)
Expand Down
2 changes: 1 addition & 1 deletion cmd/incus/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func TestShouldShow(t *testing.T) {
}

// Used by TestColumns and TestInvalidColumns.
const shorthand = "46abcdDefFlmMnNpPsStuL"
const shorthand = "46abcdDefFlmMnNpPsStuUL"
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

func TestColumns(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2472,3 +2472,7 @@ This allows the instance scriptlet to fetch a list of cluster members given an o
## `network_acl_stateless`

This adds support for stateless rules in network ACLs.

## `instance_state_started_at`

This adds a `started_at` timestamp to the instance state API.
8 changes: 8 additions & 0 deletions doc/rest-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2123,6 +2123,14 @@ definitions:
format: int64
type: integer
x-go-name: Processes
started_at:
description: |-
The time that the instance started at

API extension: instance_state_started_at.
format: date-time
type: string
x-go-name: StartedAt
status:
description: Current status (Running, Stopped, Frozen or Error)
example: Running
Expand Down
17 changes: 17 additions & 0 deletions internal/server/instance/drivers/driver_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"slices"
"sort"
"strconv"
"strings"
"sync"
"syscall"
"time"

"github.com/google/uuid"
Expand Down Expand Up @@ -1521,3 +1523,18 @@ func (d *common) setNUMANode() error {

return d.VolatileSet(map[string]string{"volatile.cpu.nodes": fmt.Sprintf("%d", node)})
}

// Gets the process starting time.
func (d *common) processStartedAt(pid int) (time.Time, error) {
file, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
if err != nil {
return time.Time{}, err
}

linuxInfo, ok := file.Sys().(*syscall.Stat_t)
if !ok {
return time.Time{}, fmt.Errorf("Bad stat type")
}

return time.Unix(linuxInfo.Ctim.Sec, linuxInfo.Ctim.Nsec), nil
}
7 changes: 7 additions & 0 deletions internal/server/instance/drivers/driver_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -3406,11 +3406,18 @@ func (d *lxc) renderState(statusCode api.StatusCode, hostInterfaces []net.Interf
processesState, _ := d.processesState(pid)

if d.isRunningStatusCode(statusCode) {
var err error

status.CPU = d.cpuState()
status.Memory = d.memoryState()
status.Network = d.networkState(hostInterfaces)
status.Pid = int64(pid)
status.Processes = processesState

status.StartedAt, err = d.processStartedAt(d.InitPID())
if err != nil {
return nil, err
}
}

status.Disk = d.diskState()
Expand Down
5 changes: 5 additions & 0 deletions internal/server/instance/drivers/driver_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -7759,6 +7759,11 @@ func (d *qemu) renderState(statusCode api.StatusCode) (*api.InstanceState, error
d.logger.Warn("Error getting disk usage", logger.Ctx{"err": err})
}

status.StartedAt, err = d.processStartedAt(d.InitPID())
if err != nil {
return status, err
}

return status, nil
}

Expand Down
1 change: 1 addition & 0 deletions internal/version/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ var APIExtensions = []string{
"instances_scriptlet_get_instances",
"instances_scriptlet_get_cluster_members",
"network_acl_stateless",
"instance_state_started_at",
}

// APIExtensionsCount returns the number of available API extensions.
Expand Down
Loading

0 comments on commit 974df22

Please sign in to comment.