From 53848dde0675c44f024bdf485579cf833f6f812d Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Wed, 9 Jul 2025 11:46:35 +0530 Subject: [PATCH 1/5] adding nvme namespace parameters Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-64k-page-output.txt | 2 +- collector/fixtures/e2e-output.txt | 2 +- collector/nvme_linux.go | 148 +++++++++++++++++++-- 3 files changed, 139 insertions(+), 13 deletions(-) diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index 107d713b72..66b321a587 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2825,7 +2825,7 @@ node_nfsd_server_rpcs_total 18628 node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge -node_nvme_info{device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 +node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 45938a2b98..ce555fc804 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2847,7 +2847,7 @@ node_nfsd_server_rpcs_total 18628 node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge -node_nvme_info{device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 +node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 diff --git a/collector/nvme_linux.go b/collector/nvme_linux.go index d1a9a87b55..88218e5ce0 100644 --- a/collector/nvme_linux.go +++ b/collector/nvme_linux.go @@ -21,14 +21,23 @@ import ( "fmt" "log/slog" "os" + "path/filepath" + "regexp" + "strings" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs/sysfs" ) type nvmeCollector struct { - fs sysfs.FS - logger *slog.Logger + fs sysfs.FS + logger *slog.Logger + namespaceInfo *prometheus.Desc + namespaceCapacityBytes *prometheus.Desc + namespaceSizeBytes *prometheus.Desc + namespaceUsedBytes *prometheus.Desc + namespaceLogicalBlockSizeBytes *prometheus.Desc + info *prometheus.Desc } func init() { @@ -42,9 +51,51 @@ func NewNVMeCollector(logger *slog.Logger) (Collector, error) { return nil, fmt.Errorf("failed to open sysfs: %w", err) } + info := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "info"), + "Non-numeric data from /sys/class/nvme/, value is always 1.", + []string{"device", "firmware_revision", "model", "serial", "state", "cntlid"}, + nil, + ) + namespaceInfo := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_info"), + "Information about NVMe namespaces. Value is always 1", + []string{"device", "nsid", "ana_state"}, nil, + ) + + namespaceCapacityBytes := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_capacity_bytes"), + "Capacity of the NVMe namespace in bytes. Computed as namespace_size * namespace_logical_block_size", + []string{"device", "nsid"}, nil, + ) + + namespaceSizeBytes := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_size_bytes"), + "Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size", + []string{"device", "nsid"}, nil, + ) + + namespaceUsedBytes := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_used_bytes"), + "Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse", + []string{"device", "nsid"}, nil, + ) + + namespaceLogicalBlockSizeBytes := prometheus.NewDesc( + prometheus.BuildFQName(namespace, "nvme", "namespace_logical_block_size_bytes"), + "Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size", + []string{"device", "nsid"}, nil, + ) + return &nvmeCollector{ - fs: fs, - logger: logger, + fs: fs, + logger: logger, + namespaceInfo: namespaceInfo, + namespaceCapacityBytes: namespaceCapacityBytes, + namespaceSizeBytes: namespaceSizeBytes, + namespaceUsedBytes: namespaceUsedBytes, + namespaceLogicalBlockSizeBytes: namespaceLogicalBlockSizeBytes, + info: info, }, nil } @@ -59,15 +110,90 @@ func (c *nvmeCollector) Update(ch chan<- prometheus.Metric) error { } for _, device := range devices { - infoDesc := prometheus.NewDesc( - prometheus.BuildFQName(namespace, "nvme", "info"), - "Non-numeric data from /sys/class/nvme/, value is always 1.", - []string{"device", "firmware_revision", "model", "serial", "state"}, - nil, - ) infoValue := 1.0 - ch <- prometheus.MustNewConstMetric(infoDesc, prometheus.GaugeValue, infoValue, device.Name, device.FirmwareRevision, device.Model, device.Serial, device.State) + + devicePath := filepath.Join(*sysPath, "class/nvme", device.Name) + ch <- prometheus.MustNewConstMetric(c.info, prometheus.GaugeValue, infoValue, device.Name, device.FirmwareRevision, device.Model, device.Serial, device.State, device.ControllerID) + // Find namespace directories. + namespacePaths, err := filepath.Glob(filepath.Join(devicePath, "nvme[0-9]*c[0-9]*n[0-9]*")) + if err != nil { + c.logger.Error("failed to list NVMe namespaces", "device", device.Name, "err", err) + continue + } + re := regexp.MustCompile(`nvme[0-9]+c[0-9]+n([0-9]+)`) + + for _, namespacePath := range namespacePaths { + + // Read namespace data. + match := re.FindStringSubmatch(filepath.Base(namespacePath)) + if len(match) == 0 { + continue + } + nsid := match[1] + nuse, err := readUintFromFile(filepath.Join(namespacePath, "nuse")) + if err != nil { + c.logger.Debug("failed to read nuse", "device", device.Name, "namespace", match[0], "err", err) + } + nsze, err := readUintFromFile(filepath.Join(namespacePath, "size")) + if err != nil { + c.logger.Debug("failed to read size", "device", device.Name, "namespace", match[0], "err", err) + } + lbaSize, err := readUintFromFile(filepath.Join(namespacePath, "queue", "logical_block_size")) + if err != nil { + c.logger.Debug("failed to read queue/logical_block_size", "device", device.Name, "namespace", match[0], "err", err) + } + ncap := nsze * lbaSize + anaState := "unknown" + anaStateSysfs, err := os.ReadFile(filepath.Join(namespacePath, "ana_state")) + if err == nil { + anaState = strings.TrimSpace(string(anaStateSysfs)) + } else { + c.logger.Debug("failed to read ana_state", "device", device.Name, "namespace", match[0], "err", err) + } + + ch <- prometheus.MustNewConstMetric( + c.namespaceInfo, + prometheus.GaugeValue, + 1.0, + device.Name, + nsid, + anaState, + ) + + ch <- prometheus.MustNewConstMetric( + c.namespaceCapacityBytes, + prometheus.GaugeValue, + float64(ncap), + device.Name, + nsid, + ) + + ch <- prometheus.MustNewConstMetric( + c.namespaceSizeBytes, + prometheus.GaugeValue, + float64(nsze), + device.Name, + nsid, + ) + + ch <- prometheus.MustNewConstMetric( + c.namespaceUsedBytes, + prometheus.GaugeValue, + float64(nuse*lbaSize), + device.Name, + nsid, + ) + + ch <- prometheus.MustNewConstMetric( + c.namespaceLogicalBlockSizeBytes, + prometheus.GaugeValue, + float64(lbaSize), + device.Name, + nsid, + ) + } } return nil } + From e252d97c6f2534ed24f7e9e871764245d137124b Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Wed, 9 Jul 2025 11:53:27 +0530 Subject: [PATCH 2/5] linting the code Signed-off-by: Shashwat Hiregoudar --- collector/nvme_linux.go | 1 - 1 file changed, 1 deletion(-) diff --git a/collector/nvme_linux.go b/collector/nvme_linux.go index 88218e5ce0..04af7276bc 100644 --- a/collector/nvme_linux.go +++ b/collector/nvme_linux.go @@ -196,4 +196,3 @@ func (c *nvmeCollector) Update(ch chan<- prometheus.Metric) error { return nil } - From be1ad0084e0e3dd12dd04aa9dfa17244bff326e8 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Tue, 30 Sep 2025 19:11:56 +0530 Subject: [PATCH 3/5] adding the namespace tests too Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-64k-page-output.txt | 15 +++++++++++++ collector/fixtures/e2e-output.txt | 15 +++++++++++++ collector/fixtures/sys.ttar | 26 ++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/collector/fixtures/e2e-64k-page-output.txt b/collector/fixtures/e2e-64k-page-output.txt index 66b321a587..4571f84dbb 100644 --- a/collector/fixtures/e2e-64k-page-output.txt +++ b/collector/fixtures/e2e-64k-page-output.txt @@ -2826,6 +2826,21 @@ node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 +# HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes. Computed as namespace_size * namespace_logical_block_size +# TYPE node_nvme_namespace_capacity_bytes gauge +node_nvme_namespace_capacity_bytes{device="nvme0",nsid="0"} 1.6e+13 +# HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 +# TYPE node_nvme_namespace_info gauge +node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 +# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size +# TYPE node_nvme_namespace_logical_block_size_bytes gauge +node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 +# HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size +# TYPE node_nvme_namespace_size_bytes gauge +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3.90625e+09 +# HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse +# TYPE node_nvme_namespace_used_bytes gauge +node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index ce555fc804..49957b71e7 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2848,6 +2848,21 @@ node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 +# HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 +# TYPE node_nvme_namespace_info gauge +node_nvme_namespace_info{device="nvme0",nsid="0",ana_state="optimized"} 1 +# HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes.Computed as namespace_size * namespace_logical_block_size +# TYPE node_nvme_namespace_capacity_bytes gauge +node_nvme_namespace_capacity_bytes{device="nvme0",nsid="0"} 1.6e+13 +# HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes.Available in /sys/class/nvme///size +# TYPE node_nvme_namespace_size_bytes gauge +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3906250000 +# HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes.Available in /sys/class/nvme///nuse +# TYPE node_nvme_namespace_used_bytes gauge +node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 488281250 +# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes.Usually 4Kb.Available in /sys/class/nvme///queue/logical_block_size +# TYPE node_nvme_namespace_logical_block_size_bytes gauge +node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1 diff --git a/collector/fixtures/sys.ttar b/collector/fixtures/sys.ttar index 4e20c2a324..c30ba125c5 100644 --- a/collector/fixtures/sys.ttar +++ b/collector/fixtures/sys.ttar @@ -2199,6 +2199,32 @@ Lines: 1 live Mode: 444 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/nvme/nvme0/nvme0c0n0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/nvme/nvme0/nvme0c0n0/ana_state +Lines: 1 +optimized +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/nvme/nvme0/nvme0c0n0/size +Lines: 1 +3906250000 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/nvme/nvme0/nvme0c0n0/nuse +Lines: 1 +488281250 +Mode: 444 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: sys/class/nvme/nvme0/nvme0c0n0/queue +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: sys/class/nvme/nvme0/nvme0c0n0/queue/logical_block_size +Lines: 1 +4096 +Mode: 644 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: sys/class/power_supply Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 1213dcc3237dac4bddd8ae6d517322676d43b942 Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Wed, 1 Oct 2025 09:45:41 +0530 Subject: [PATCH 4/5] correcting the formatting Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-output.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index 49957b71e7..fdcb1a5e5b 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2850,17 +2850,17 @@ node_nfsd_server_threads 8 node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 # HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 # TYPE node_nvme_namespace_info gauge -node_nvme_namespace_info{device="nvme0",nsid="0",ana_state="optimized"} 1 -# HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes.Computed as namespace_size * namespace_logical_block_size +node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 +# HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes. Computed as namespace_size * namespace_logical_block_size # TYPE node_nvme_namespace_capacity_bytes gauge node_nvme_namespace_capacity_bytes{device="nvme0",nsid="0"} 1.6e+13 -# HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes.Available in /sys/class/nvme///size +# HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size # TYPE node_nvme_namespace_size_bytes gauge -node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3906250000 -# HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes.Available in /sys/class/nvme///nuse +node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3.90625e+09 +# HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse # TYPE node_nvme_namespace_used_bytes gauge -node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 488281250 -# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes.Usually 4Kb.Available in /sys/class/nvme///queue/logical_block_size +node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 +# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size # TYPE node_nvme_namespace_logical_block_size_bytes gauge node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. From d99435f32dfb0211821daf71c7ae1a557abe68ae Mon Sep 17 00:00:00 2001 From: Shashwat Hiregoudar Date: Wed, 1 Oct 2025 09:54:12 +0530 Subject: [PATCH 5/5] correcting the order too Signed-off-by: Shashwat Hiregoudar --- collector/fixtures/e2e-output.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/collector/fixtures/e2e-output.txt b/collector/fixtures/e2e-output.txt index fdcb1a5e5b..9a151823f2 100644 --- a/collector/fixtures/e2e-output.txt +++ b/collector/fixtures/e2e-output.txt @@ -2848,21 +2848,21 @@ node_nfsd_server_threads 8 # HELP node_nvme_info Non-numeric data from /sys/class/nvme/, value is always 1. # TYPE node_nvme_info gauge node_nvme_info{cntlid="1997",device="nvme0",firmware_revision="1B2QEXP7",model="Samsung SSD 970 PRO 512GB",serial="S680HF8N190894I",state="live"} 1 -# HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 -# TYPE node_nvme_namespace_info gauge -node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 # HELP node_nvme_namespace_capacity_bytes Capacity of the NVMe namespace in bytes. Computed as namespace_size * namespace_logical_block_size # TYPE node_nvme_namespace_capacity_bytes gauge node_nvme_namespace_capacity_bytes{device="nvme0",nsid="0"} 1.6e+13 +# HELP node_nvme_namespace_info Information about NVMe namespaces. Value is always 1 +# TYPE node_nvme_namespace_info gauge +node_nvme_namespace_info{ana_state="optimized",device="nvme0",nsid="0"} 1 +# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size +# TYPE node_nvme_namespace_logical_block_size_bytes gauge +node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_nvme_namespace_size_bytes Size of the NVMe namespace in bytes. Available in /sys/class/nvme///size # TYPE node_nvme_namespace_size_bytes gauge node_nvme_namespace_size_bytes{device="nvme0",nsid="0"} 3.90625e+09 # HELP node_nvme_namespace_used_bytes Used space of the NVMe namespace in bytes. Available in /sys/class/nvme///nuse # TYPE node_nvme_namespace_used_bytes gauge node_nvme_namespace_used_bytes{device="nvme0",nsid="0"} 2e+12 -# HELP node_nvme_namespace_logical_block_size_bytes Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme///queue/logical_block_size -# TYPE node_nvme_namespace_logical_block_size_bytes gauge -node_nvme_namespace_logical_block_size_bytes{device="nvme0",nsid="0"} 4096 # HELP node_os_info A metric with a constant '1' value labeled by build_id, id, id_like, image_id, image_version, name, pretty_name, variant, variant_id, version, version_codename, version_id. # TYPE node_os_info gauge node_os_info{build_id="",id="ubuntu",id_like="debian",image_id="",image_version="",name="Ubuntu",pretty_name="Ubuntu 20.04.2 LTS",variant="",variant_id="",version="20.04.2 LTS (Focal Fossa)",version_codename="focal",version_id="20.04"} 1