From 8296e49043dafaeda8d2d66f7a958136a6dfd888 Mon Sep 17 00:00:00 2001 From: KanagiAomori Date: Mon, 23 Aug 2021 12:33:58 +0200 Subject: [PATCH 1/3] JSON API - ioctl handling, json requests handling, structuring and marshaling json responses, RESTful API early prototype Signed-off-by: Piotr Debski --- json/go.mod | 3 + json/ioctl/ioctl.go | 77 +++++++++++++ json/ioctl/json_structs.go | 156 +++++++++++++++++++++++++++ json/ioctl/marshal.go | 37 +++++++ json/ioctl/ocf_env_headers.h | 22 ++++ json/ioctl/ocf_types.go | 14 +++ json/ioctl/strut_conv.go | 202 +++++++++++++++++++++++++++++++++++ json/main.go | 47 ++++++++ json/request.json | 12 +++ json/rest/compose_data.go | 53 +++++++++ json/rest/request.go | 53 +++++++++ json/rest/rest.go | 40 +++++++ 12 files changed, 716 insertions(+) create mode 100644 json/go.mod create mode 100644 json/ioctl/ioctl.go create mode 100644 json/ioctl/json_structs.go create mode 100644 json/ioctl/marshal.go create mode 100644 json/ioctl/ocf_env_headers.h create mode 100644 json/ioctl/ocf_types.go create mode 100644 json/ioctl/strut_conv.go create mode 100644 json/main.go create mode 100644 json/request.json create mode 100644 json/rest/compose_data.go create mode 100644 json/rest/request.go create mode 100644 json/rest/rest.go diff --git a/json/go.mod b/json/go.mod new file mode 100644 index 000000000..8228b8b67 --- /dev/null +++ b/json/go.mod @@ -0,0 +1,3 @@ +module example.com/json + +go 1.13 diff --git a/json/ioctl/ioctl.go b/json/ioctl/ioctl.go new file mode 100644 index 000000000..f619bd3db --- /dev/null +++ b/json/ioctl/ioctl.go @@ -0,0 +1,77 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +package ioctl + +// #cgo CFLAGS: -I./../../modules/include/ -I/ocf_env_headers.h +/* +#include +*/ +import "C" +import ( + "log" + "os" + "syscall" + "unsafe" +) + +/** Get CAS exported device file descriptor **/ + +func Read_fd() uintptr { + file, err := os.Open("/dev/cas_ctrl") + if err != nil { + log.Fatal(err) + } + return file.Fd() +} + +/** Use syscall ioctl to get get_kcas_stats structs **/ + +func Ioctl_get_kcas_stats(fd uintptr, cache_id, core_id, part_id uint16) C.struct_kcas_get_stats { + C_kstats := C.struct_kcas_get_stats{cache_id: C.ushort(cache_id), + core_id: C.ushort(core_id), part_id: C.ushort(part_id)} + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_GET_STATS, + uintptr(unsafe.Pointer(&C_kstats))) + if err != 0 { + log.Fatal(err) + } + return C_kstats +} + +/** Use syscall ioctl to get cache info struct **/ + +func Ioctl_cache_info(fd uintptr, cache_id uint16) C.struct_kcas_cache_info { + C_kcache_info := C.struct_kcas_cache_info{cache_id: C.ushort(cache_id)} + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_CACHE_INFO, + uintptr(unsafe.Pointer(&C_kcache_info))) + if err != 0 { + log.Fatal(err) + } + return C_kcache_info +} + +/** Use syscall ioctl to get core info struct **/ + +func Ioctl_core_info(fd uintptr, cache_id, core_id uint16) C.struct_kcas_core_info { + C_kcore_info := C.struct_kcas_core_info{cache_id: C.ushort(cache_id), core_id: C.ushort(core_id)} + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_CORE_INFO, + uintptr(unsafe.Pointer(&C_kcore_info))) + if err != 0 { + log.Fatal(err) + } + return C_kcore_info +} + +/** Use syscall ioctl to get io class struct **/ + +func Ioctl_io_class(fd uintptr, cache_id, class_id uint16) C.struct_kcas_io_class { + C_kio_class := C.struct_kcas_io_class{cache_id: C.ushort(cache_id), class_id: C.uint(class_id)} + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_PARTITION_INFO, + uintptr(unsafe.Pointer(&C_kio_class))) + if err != 0 { + log.Fatal(err) + } + return C_kio_class +} diff --git a/json/ioctl/json_structs.go b/json/ioctl/json_structs.go new file mode 100644 index 000000000..e77514e6d --- /dev/null +++ b/json/ioctl/json_structs.go @@ -0,0 +1,156 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +package ioctl + +/* +#include +*/ +import "C" + +/** IOCTL_KCAS_GET_STATS structs **/ + +type Kcas_get_stats struct { + Cache_id uint16 `json:"Cache id"` + Core_id uint16 `json:"Core id"` + IO_class_id uint16 `json:"Part id"` + Usage Ocf_stats_usage `json:"Usage"` + Req Ocf_stats_requests `json:"Requests"` + Blocks Ocf_stats_blocks `json:"Blocks"` + Errors Ocf_stats_errors `json:"Errors"` + ext_err_code int +} + +type Ocf_stats_usage struct { + Occupancy Ocf_stat `json:"Occupancy"` + Free Ocf_stat `json:"Free"` + Clean Ocf_stat `json:"Clean"` + Dirty Ocf_stat `json:"Dirty"` +} + +type Ocf_stats_requests struct { + Rd_hits Ocf_stat `json:"Read hits"` + Rd_partial_misses Ocf_stat `json:"Read partial misses"` + Rd_full_misses Ocf_stat `json:"Read full misses"` + Rd_total Ocf_stat `json:"Read total"` + Wr_hits Ocf_stat `json:"Write hits"` + Wr_partial_misses Ocf_stat `json:"Write partial misses"` + Wr_full_misses Ocf_stat `json:"Write full misses"` + Wr_total Ocf_stat `json:"Write total"` + Rd_pt Ocf_stat `json:"Pass-Trough reads"` + Wr_pt Ocf_stat `json:"Pass-Trough writes"` + Serviced Ocf_stat `json:"Serviced requests"` + Total Ocf_stat `json:"Total requests"` +} + +type Ocf_stats_blocks struct { + Core_volume_rd Ocf_stat `json:"Reads from core(s)"` + Core_volume_wr Ocf_stat `json:"Writes from core(s)"` + Core_volume_total Ocf_stat `json:"Total to/from core(s)"` + Cache_volume_rd Ocf_stat `json:"Reads from cache(s)"` + Cache_volume_wr Ocf_stat `json:"Writes from cache(s)"` + Cache_volume_total Ocf_stat `json:"Total to/from cache(s)"` + Volume_rd Ocf_stat `json:"Reads from exported object(s)"` + Volume_wr Ocf_stat `json:"Writes from exported object(s)"` + Volume_total Ocf_stat `json:"Total to/from exported object(s)"` +} + +type Ocf_stats_errors struct { + Core_volume_rd Ocf_stat `json:"Core read errors"` + Core_volume_wr Ocf_stat `json:"Core write errors"` + Core_volume_total Ocf_stat `json:"Core total errors"` + Cache_volume_rd Ocf_stat `json:"Cache read errors"` + Cache_volume_wr Ocf_stat `json:"Cache write errors"` + Cache_volume_total Ocf_stat `json:"Cache total errors"` + Total Ocf_stat `json:"Total errors"` +} + +type Ocf_stat struct { + Value uint64 `json:"page"` + Fraction uint64 `json:"fraction"` +} + +/** IOCTL_CACHE_INFO structs **/ + +type Kcas_cache_info struct { + Cache_id uint16 `json:"Cache id"` + Cache_path_name string `json:"Cache device"` + Core_id []uint16 `json:"Core(s) id(s)"` + Info Ocf_cache_info `json:"Cache details"` + Metadata_mode string `json:"Metadata mode"` + ext_err_code int +} + +type Ocf_cache_info struct { + Attached bool `json:"Attached"` + Volume_type uint8 `json:"Volume type"` // mby enum -> convert to str? + State string `json:"Status"` + Size uint32 `json:"Size [cache lines]"` + Inactive_cores Core_stats `json:"Inactive cores"` + Occupancy uint32 `json:"Occupancy [cache lines]"` + Dirty uint32 `json:"Dirty [cache lines]"` + Dirty_for uint64 `json:"Dirty for [s]"` + Dirty_initial uint32 `json:"Initially dirty [cache lines]"` + Cache_mode string `json:"Cache mode"` + Fallback_pt Fallback_pt_stats `json:"Pass-Trough fallback statistics"` + Cleaning_policy string `json:"Cleaning policy"` + Promotion_policy string `json:"Promotion policy"` + Cache_line_size uint64 `json:"Cache line size [KiB]"` + Flushed uint32 `json:"Flushed blocks"` + Core_count uint32 `json:"Core count"` + Metadata_footprint uint64 `json:"Metadata footprint [B]"` + Metadata_end_offset uint64 `json:"Metadata end offset [4 KiB blocks]"` +} + +type Core_stats struct { + Occupancy Ocf_stat `json:"Occupancy"` + Clean Ocf_stat `json:"Clean"` + Dirty Ocf_stat `json:"Dirty"` +} + +type Fallback_pt_stats struct { + Error_counter int `json:"IO errors count"` + Status bool `json:"Status"` +} + +/** IOCTL_CORE_INFO structs **/ + +type Kcas_core_info struct { + Core_path_name string `json:"Core path"` + Cache_id uint16 `json:"Cache id"` + Core_id uint16 `json:"Core id"` + Info Ocf_core_info `json:"Core details"` + State string `json:"State"` + ext_err_code int +} + +type Ocf_core_info struct { + Core_size uint64 `json:"Core size [line size]"` + Core_size_bytes uint64 `json:"Core size [B]"` + Flushed uint32 `json:"Flushed blocks"` + Dirty uint32 `json:"Dirty blocks"` + Dirty_for uint64 `json:"Dirty for [s]"` + Seq_cutoff_threshold uint32 `json:"Sequential cutoff threshold [B]"` + Seq_cutoff_policy string `json:"Sequential cutoff policy [B]"` +} + +/** IOCTL_KCAS_IO_CLASS structs **/ + +type Kcas_io_class struct { + Cache_id uint16 `json:"Cache id"` + Class_id uint32 `json:"Class id"` + Info Ocf_io_class_info "IO class details" + ext_err_code int +} + +type Ocf_io_class_info struct { + Name string `json:"Name"` + Cache_mode string `json:"Cache mode"` + Priority int16 `json:"Priority"` + Curr_size uint32 `json:"Current size [cache line]"` + Min_size int32 `json:"Min size [cache line]"` + Max_size int32 `json:"Max size [cache line]"` + Cleaning_policy string `json:"Cleaning policy"` +} diff --git a/json/ioctl/marshal.go b/json/ioctl/marshal.go new file mode 100644 index 000000000..026a6c81d --- /dev/null +++ b/json/ioctl/marshal.go @@ -0,0 +1,37 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +package ioctl + +import ( + "encoding/json" + "fmt" +) + +/** Marshal GoStructs into JSON **/ + +func Marshal_kcas_stats(kstats Kcas_get_stats) ([]byte, error) { + kstats_b, err := json.MarshalIndent(kstats, "", " ") + fmt.Println("Statistics: ", string(kstats_b), "\n") + return kstats_b, err +} + +func Marshal_kcache_info(kcache_info Kcas_cache_info) ([]byte, error) { + kcache_info_b, err := json.MarshalIndent(kcache_info, "", " ") + fmt.Println("Cache info: ", string(kcache_info_b), "\n") + return kcache_info_b, err +} + +func Marshal_kcore_info(kcore_info Kcas_core_info) ([]byte, error) { + kcore_info_b, err := json.MarshalIndent(kcore_info, "", " ") + fmt.Println("Core info: ", string(kcore_info_b), "\n") + return kcore_info_b, err +} + +func Marshal_kio_class(kio_class Kcas_io_class) ([]byte, error) { + kio_class_b, err := json.MarshalIndent(kio_class, "", " ") + fmt.Println("IO class", string(kio_class_b), "\n") + return kio_class_b, err +} diff --git a/json/ioctl/ocf_env_headers.h b/json/ioctl/ocf_env_headers.h new file mode 100644 index 000000000..c26519cd5 --- /dev/null +++ b/json/ioctl/ocf_env_headers.h @@ -0,0 +1,22 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +#ifndef __OCF_ENV_HEADERS_H__ +#define __OCF_ENV_HEADERS_H__ + +#include +#include +#include + +/* TODO: Move prefix printing to context logger. */ +#define OCF_LOGO "CAS" +#define OCF_PREFIX_SHORT "[" OCF_LOGO "] " +#define OCF_PREFIX_LONG "Cache Acceleration Software Linux" + +#define OCF_VERSION_MAIN CAS_VERSION_MAIN +#define OCF_VERSION_MAJOR CAS_VERSION_MAJOR +#define OCF_VERSION_MINOR CAS_VERSION_MINOR + +#endif /* __OCF_ENV_HEADERS_H__ */ diff --git a/json/ioctl/ocf_types.go b/json/ioctl/ocf_types.go new file mode 100644 index 000000000..d662c0ce8 --- /dev/null +++ b/json/ioctl/ocf_types.go @@ -0,0 +1,14 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear + */ + +package ioctl + +var ocf_cache_state_str = [...]string{"running", "stopping", "initializing", "incomplete", "max"} +var ocf_core_state_str = [...]string{"active", "inactive", "max"} +var ocf_cache_mode_str = [...]string{"wt", "wb", "wa", "pt", "wi", "wo", "max"} +var ocf_cleaning_str = [...]string{"nop", "alru", "acp", "max"} +var metadata_mode_str = [...]string{"invalid", "normal", "atomic"} +var ocf_seq_cutoff_policy_str = [...]string{"always", "full", "never", "max"} +var ocf_promotion_str = [...]string{"always", "nhit", "max"} diff --git a/json/ioctl/strut_conv.go b/json/ioctl/strut_conv.go new file mode 100644 index 000000000..789c891c0 --- /dev/null +++ b/json/ioctl/strut_conv.go @@ -0,0 +1,202 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +package ioctl + +// #cgo CFLAGS: -I./../../modules/include/ -I/Ocf_env_headers.h +/* +#include +*/ +import "C" +import ( + "unsafe" +) + +/** kcas_get_stats struct conversion C to Go **/ + +func Conv_stats(C_kstats C.struct_kcas_get_stats) Kcas_get_stats { + res := Kcas_get_stats{ + Cache_id: uint16(C_kstats.cache_id), + Core_id: uint16(C_kstats.core_id), + IO_class_id: uint16(C_kstats.part_id), + Usage: Ocf_stats_usage{ + Occupancy: Ocf_stat{Value: uint64(C_kstats.usage.occupancy.value), + Fraction: uint64(C_kstats.usage.occupancy.fraction)}, + Free: Ocf_stat{Value: uint64(C_kstats.usage.free.value), + Fraction: uint64(C_kstats.usage.free.fraction)}, + Clean: Ocf_stat{Value: uint64(C_kstats.usage.clean.value), + Fraction: uint64(C_kstats.usage.clean.fraction)}, + Dirty: Ocf_stat{Value: uint64(C_kstats.usage.dirty.value), + Fraction: uint64(C_kstats.usage.dirty.fraction)}, + }, + Req: Ocf_stats_requests{ + Rd_hits: Ocf_stat{Value: uint64(C_kstats.req.rd_hits.value), + Fraction: uint64(C_kstats.req.rd_hits.fraction)}, + Rd_partial_misses: Ocf_stat{Value: uint64(C_kstats.req.rd_partial_misses.value), + Fraction: uint64(C_kstats.req.rd_partial_misses.fraction)}, + Rd_full_misses: Ocf_stat{Value: uint64(C_kstats.req.rd_full_misses.value), + Fraction: uint64(C_kstats.req.rd_full_misses.fraction)}, + Rd_total: Ocf_stat{Value: uint64(C_kstats.req.rd_total.value), + Fraction: uint64(C_kstats.req.rd_total.fraction)}, + Wr_hits: Ocf_stat{Value: uint64(C_kstats.req.wr_hits.value), + Fraction: uint64(C_kstats.req.wr_hits.fraction)}, + Wr_partial_misses: Ocf_stat{Value: uint64(C_kstats.req.wr_partial_misses.value), + Fraction: uint64(C_kstats.req.wr_partial_misses.fraction)}, + Wr_full_misses: Ocf_stat{Value: uint64(C_kstats.req.wr_full_misses.value), Fraction: uint64(C_kstats.req.wr_full_misses.fraction)}, + Wr_total: Ocf_stat{Value: uint64(C_kstats.req.wr_total.value), + Fraction: uint64(C_kstats.req.wr_total.fraction)}, + Rd_pt: Ocf_stat{Value: uint64(C_kstats.req.rd_pt.value), + Fraction: uint64(C_kstats.req.rd_pt.fraction)}, + Wr_pt: Ocf_stat{Value: uint64(C_kstats.req.wr_pt.value), + Fraction: uint64(C_kstats.req.wr_pt.fraction)}, + Serviced: Ocf_stat{Value: uint64(C_kstats.req.serviced.value), + Fraction: uint64(C_kstats.req.serviced.fraction)}, + Total: Ocf_stat{Value: uint64(C_kstats.req.total.value), + Fraction: uint64(C_kstats.req.total.fraction)}, + }, + Blocks: Ocf_stats_blocks{ + Core_volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_rd.value), + Fraction: uint64(C_kstats.blocks.core_volume_rd.fraction)}, + Core_volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_wr.value), + Fraction: uint64(C_kstats.blocks.core_volume_wr.fraction)}, + Core_volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_total.value), + Fraction: uint64(C_kstats.blocks.core_volume_total.fraction)}, + Cache_volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_rd.value), + Fraction: uint64(C_kstats.blocks.cache_volume_rd.fraction)}, + Cache_volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_wr.value), + Fraction: uint64(C_kstats.blocks.cache_volume_wr.fraction)}, + Cache_volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_total.value), Fraction: uint64(C_kstats.blocks.cache_volume_total.fraction)}, + Volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.volume_rd.value), + Fraction: uint64(C_kstats.blocks.volume_rd.fraction)}, + Volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.volume_wr.value), + Fraction: uint64(C_kstats.blocks.volume_wr.fraction)}, + Volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.volume_total.value), + Fraction: uint64(C_kstats.blocks.volume_total.fraction)}, + }, + Errors: Ocf_stats_errors{ + Core_volume_rd: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_rd.value), + Fraction: uint64(C_kstats.errors.core_volume_rd.fraction)}, + Core_volume_wr: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_wr.value), + Fraction: uint64(C_kstats.errors.core_volume_wr.fraction)}, + Core_volume_total: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_total.value), + Fraction: uint64(C_kstats.errors.core_volume_total.fraction)}, + Cache_volume_rd: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_rd.value), + Fraction: uint64(C_kstats.errors.cache_volume_rd.fraction)}, + Cache_volume_wr: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_wr.value), + Fraction: uint64(C_kstats.errors.cache_volume_wr.fraction)}, + Cache_volume_total: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_total.value), + Fraction: uint64(C_kstats.errors.cache_volume_total.fraction)}, + Total: Ocf_stat{Value: uint64(C_kstats.errors.total.value), + Fraction: uint64(C_kstats.errors.total.fraction)}, + }, + } + return res +} + +/** cache_info struct conversion C to Go **/ + +func Conv_cache_info(C_cache_info C.struct_kcas_cache_info) Kcas_cache_info { + + var core_id []uint16 + for _, v := range (*[C.OCF_CORE_MAX]uint16)(unsafe.Pointer(&C_cache_info.core_id[0]))[:C.OCF_CORE_MAX:C.OCF_CORE_MAX] { + if v > 0 { + core_id = append(core_id, v) + } + } + var cache_mode string + if 0 <= C_cache_info.info.cache_mode { + cache_mode = ocf_cache_mode_str[C_cache_info.info.cache_mode] + } else { + cache_mode = "none" + } + + res := Kcas_cache_info{ + Cache_id: uint16(C_cache_info.cache_id), + Cache_path_name: C.GoString(&C_cache_info.cache_path_name[0]), + Core_id: core_id, + Info: Ocf_cache_info{ + Attached: bool(C_cache_info.info.attached), + Volume_type: uint8(C_cache_info.info.volume_type), + State: ocf_cache_state_str[C_cache_info.info.state], + Size: uint32(C_cache_info.info.size), + Inactive_cores: Core_stats{ + Occupancy: Ocf_stat{Value: uint64(C_cache_info.info.inactive.occupancy.value), + Fraction: uint64(C_cache_info.info.inactive.occupancy.fraction)}, + Clean: Ocf_stat{Value: uint64(C_cache_info.info.inactive.clean.value), + Fraction: uint64(C_cache_info.info.inactive.clean.fraction)}, + Dirty: Ocf_stat{Value: uint64(C_cache_info.info.inactive.dirty.value), + Fraction: uint64(C_cache_info.info.inactive.dirty.fraction)}, + }, + Occupancy: uint32(C_cache_info.info.occupancy), + Dirty: uint32(C_cache_info.info.dirty), + Dirty_for: uint64(C_cache_info.info.dirty_for), + Dirty_initial: uint32(C_cache_info.info.dirty_initial), + Cache_mode: cache_mode, + Fallback_pt: Fallback_pt_stats{ + Error_counter: int(C_cache_info.info.fallback_pt.error_counter), + Status: bool(C_cache_info.info.fallback_pt.status), + }, + Cleaning_policy: ocf_cleaning_str[C_cache_info.info.cleaning_policy], + Promotion_policy: ocf_promotion_str[C_cache_info.info.promotion_policy], + Cache_line_size: uint64(C_cache_info.info.cache_line_size), + Flushed: uint32(C_cache_info.info.flushed), + Core_count: uint32(C_cache_info.info.core_count), + Metadata_footprint: uint64(C_cache_info.info.metadata_footprint), + Metadata_end_offset: uint64(C_cache_info.info.metadata_end_offset), + }, + Metadata_mode: metadata_mode_str[C_cache_info.metadata_mode], + ext_err_code: int(C_cache_info.ext_err_code), + } + return res +} + +/** core_info struct conversion C to Go **/ + +func Conv_core_info(kcore_info C.struct_kcas_core_info) Kcas_core_info { + + res := Kcas_core_info{ + Core_path_name: C.GoString(&kcore_info.core_path_name[0]), + Cache_id: uint16(kcore_info.cache_id), + Core_id: uint16(kcore_info.core_id), + Info: Ocf_core_info{ + Core_size: uint64(kcore_info.info.core_size), + Core_size_bytes: uint64(kcore_info.info.core_size_bytes), + Flushed: uint32(kcore_info.info.flush_operation.flushed), + Dirty: uint32(kcore_info.info.flush_operation.dirty), + Dirty_for: uint64(kcore_info.info.dirty_for), + Seq_cutoff_threshold: uint32(kcore_info.info.seq_cutoff_threshold), + Seq_cutoff_policy: ocf_seq_cutoff_policy_str[kcore_info.info.seq_cutoff_policy], + }, + State: ocf_core_state_str[kcore_info.state], + } + return res +} + +/** io_class struct conversion C to Go **/ + +func Conv_io_class(kio_class C.struct_kcas_io_class) Kcas_io_class { + var cache_mode string + if 0 <= kio_class.info.cache_mode { + cache_mode = ocf_cache_mode_str[kio_class.info.cache_mode] + } else { + cache_mode = "none" + } + + res := Kcas_io_class{ + Cache_id: uint16(kio_class.cache_id), + Class_id: uint32(kio_class.class_id), + Info: Ocf_io_class_info{ + Name: C.GoString(&kio_class.info.name[0]), + Cache_mode: cache_mode, + Priority: int16(kio_class.info.priority), + Curr_size: uint32(kio_class.info.curr_size), + Min_size: int32(kio_class.info.min_size), + Max_size: int32(kio_class.info.max_size), + Cleaning_policy: ocf_cleaning_str[kio_class.info.cleaning_policy_type], + }, + ext_err_code: int(kio_class.ext_err_code), + } + return res +} diff --git a/json/main.go b/json/main.go new file mode 100644 index 000000000..62b9d70cd --- /dev/null +++ b/json/main.go @@ -0,0 +1,47 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-Clause-Clear +*/ + +/* + * Prototype of JSON RESTful API + */ + +package main + +import ( + "fmt" + + "example.com/json/ioctl" + "example.com/json/rest" +) + +func main() { + fmt.Println("open-cas-linux-RestAPI") + + // test RestAPI + + // test request json file + var req rest.Request + req.Read_req() + req.Show_req() + req.Write_req() + + // get file descriptor + fd := ioctl.Read_fd() + + // JSON requests control + if req.Req_list_caches { + rest.List_caches(fd) + } + + if req.Req_get_stats { + rest.Get_stats(fd, req.Get_stats) + } + + // test cmd flags + //rest.RestAPI() + //rest.Flags() + /* + */ +} diff --git a/json/request.json b/json/request.json new file mode 100644 index 000000000..37377c1c1 --- /dev/null +++ b/json/request.json @@ -0,0 +1,12 @@ +{ + "Requested list cache(s) and core(s)": false, + "Requested get statistics": true, + "Get statistics": { + "cache id": 1, + "Core id": 1, + "IO class": 0, + "Requested cache info": true, + "Requested core info": true, + "Requested io classinfo": true + } +} \ No newline at end of file diff --git a/json/rest/compose_data.go b/json/rest/compose_data.go new file mode 100644 index 000000000..e3fd8be95 --- /dev/null +++ b/json/rest/compose_data.go @@ -0,0 +1,53 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +package rest + +import ( + "fmt" + + "example.com/json/ioctl" +) + +func List_caches(fd uintptr) { + fmt.Println("\nLIST_CACHES_WITH_CORES\n") + + C_cache_info := ioctl.Ioctl_cache_info(fd, 1) + cache_info := ioctl.Conv_cache_info(C_cache_info) + ioctl.Marshal_kcache_info(cache_info) + + for _, v := range cache_info.Core_id { + C_kcore_info := ioctl.Ioctl_core_info(fd, cache_info.Cache_id, v) + kcore_info := ioctl.Conv_core_info(C_kcore_info) + ioctl.Marshal_kcore_info(kcore_info) + } +} + +func Get_stats(fd uintptr, req_get_stats Stats_info) { + fmt.Println("\nGET_STATS\n") + + if req_get_stats.Req_cache_info { + C_cache_info := ioctl.Ioctl_cache_info(fd, req_get_stats.Cache_id) + kcache_info := ioctl.Conv_cache_info(C_cache_info) + ioctl.Marshal_kcache_info(kcache_info) + } + + if req_get_stats.Req_core_info { + C_kcore_info := ioctl.Ioctl_core_info(fd, req_get_stats.Cache_id, req_get_stats.Core_id) + kcore_info := ioctl.Conv_core_info(C_kcore_info) + ioctl.Marshal_kcore_info(kcore_info) + } + + if req_get_stats.Req_io_class_info { + C_kio_class := ioctl.Ioctl_io_class(fd, req_get_stats.Cache_id, uint16(req_get_stats.Io_class)) + kio_class := ioctl.Conv_io_class(C_kio_class) + ioctl.Marshal_kio_class(kio_class) + } + + C_kstats := ioctl.Ioctl_get_kcas_stats(fd, req_get_stats.Cache_id, + req_get_stats.Core_id, uint16(req_get_stats.Io_class)) + kstats := ioctl.Conv_stats(C_kstats) + ioctl.Marshal_kcas_stats(kstats) +} diff --git a/json/rest/request.go b/json/rest/request.go new file mode 100644 index 000000000..143664fdf --- /dev/null +++ b/json/rest/request.go @@ -0,0 +1,53 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +package rest + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" +) + +type Request struct { + Req_list_caches bool `json:"Requested list cache(s) and core(s)"` + Req_get_stats bool `json:"Requested get statistics"` + Get_stats Stats_info `json:"Get statistics"` +} + +type Stats_info struct { + Cache_id uint16 `json:"cache id"` + Core_id uint16 `json:"Core id"` + Io_class uint32 `json:"IO class"` + Req_cache_info bool `json:"Requested cache info"` + Req_core_info bool `json:"Requested core info"` + Req_io_class_info bool `json:"Requested io classinfo"` +} + +/** debug read and write json requests */ +func (req *Request) Write_req() { + file, err := json.MarshalIndent(req, "", " ") + _ = ioutil.WriteFile("request.json", file, 0644) + if err != nil { + log.Fatal(err) + } +} + +func (req *Request) Read_req() { + data, err := ioutil.ReadFile("request.json") + if err != nil { + log.Fatal(err) + } + json.Unmarshal(data, req) +} + +func (req *Request) Show_req() { + req_b, err := json.MarshalIndent(req, "", " ") + if err != nil { + log.Fatal(err) + } + fmt.Print("Request: ", string(req_b), "\n") +} diff --git a/json/rest/rest.go b/json/rest/rest.go new file mode 100644 index 000000000..aeee0a08f --- /dev/null +++ b/json/rest/rest.go @@ -0,0 +1,40 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause-Clear +*/ + +package rest + +import ( + "encoding/json" + "fmt" + "log" + "net/http" +) + +func RestAPI() { + handleRequests() +} + +func homePage(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Open CAS - RestAPI") + fmt.Print("Endpoint hit: homePage") +} + +func handleRequests() { + http.HandleFunc("/", homePage) + + /** handling requests */ + http.HandleFunc("/request", returnRequest) + //http.HandleFunc("/get_stats", ) + //http.HandleFunc("/list_caches", ) + + log.Fatal(http.ListenAndServe(":10000", nil)) +} + +func returnRequest(w http.ResponseWriter, r *http.Request) { + fmt.Println("Endpoint hit: returnRequest") + var req Request + req.Read_req() + json.NewEncoder(w).Encode(req) +} From 72a54ad0540147e3374b655a15cc02091ae3a0b3 Mon Sep 17 00:00:00 2001 From: KanagiAomori Date: Mon, 23 Aug 2021 12:33:58 +0200 Subject: [PATCH 2/3] JSON API - stage I Signed-off-by: Piotr Debski --- configure | 12 ++ json/Makefile | 24 ++++ json/api/compose_data.go | 165 +++++++++++++++++++++++ json/api/error.go | 62 +++++++++ json/api/json_api.go | 210 +++++++++++++++++++++++++++++ json/api/request.go | 206 ++++++++++++++++++++++++++++ json/go.mod | 7 +- json/ioctl/ioctl.go | 91 ++++++++----- json/ioctl/json_structs.go | 44 +++--- json/ioctl/marshal.go | 37 ----- json/ioctl/ocf_env_headers.h | 22 --- json/ioctl/ocf_types.go | 27 ++-- json/ioctl/structs_conv.go | 255 +++++++++++++++++++++++++++++++++++ json/ioctl/strut_conv.go | 202 --------------------------- json/main.go | 41 +----- json/request.json | 16 +-- json/rest/compose_data.go | 53 -------- json/rest/request.go | 53 -------- json/rest/rest.go | 40 ------ 19 files changed, 1044 insertions(+), 523 deletions(-) create mode 100644 json/Makefile create mode 100644 json/api/compose_data.go create mode 100644 json/api/error.go create mode 100644 json/api/json_api.go create mode 100644 json/api/request.go delete mode 100644 json/ioctl/marshal.go delete mode 100644 json/ioctl/ocf_env_headers.h create mode 100644 json/ioctl/structs_conv.go delete mode 100644 json/ioctl/strut_conv.go delete mode 100644 json/rest/compose_data.go delete mode 100644 json/rest/request.go delete mode 100644 json/rest/rest.go diff --git a/configure b/configure index cb7e7762b..9e5723d48 100755 --- a/configure +++ b/configure @@ -139,6 +139,18 @@ generate_header() { done } +while (( $# )); do + case "$1" in + --with-json-api) + if ! grep json Makefile >/dev/null; then + sed -i '/^DIRS/ s/$/ json/' Makefile + fi + ;; + esac + shift +done + + if [ -z "$1" ]; then generate_config else diff --git a/json/Makefile b/json/Makefile new file mode 100644 index 000000000..591562262 --- /dev/null +++ b/json/Makefile @@ -0,0 +1,24 @@ +# +# Copyright(c) 2012-2021 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# + +include ../tools/helpers.mk + +BINARY_PATH = /sbin + +all: + @mkdir -p bin + @go build -o bin/opencas-json-api main.go +install: + @echo " Installing JSON API " + @install -m 755 bin/opencas-json-api $(DESTDIR)/sbin/opencas-json-api + +uninstall: + @echo " Uninstalling JSON API " + @$(call remove-file,$(DESTDIR)/sbin/opencas-json-api) + +clean: + @rm -f bin/opencas-json-api + +.PHONY: all clean install uninstall diff --git a/json/api/compose_data.go b/json/api/compose_data.go new file mode 100644 index 000000000..b9dea49aa --- /dev/null +++ b/json/api/compose_data.go @@ -0,0 +1,165 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause + */ + +package api + +import ( + "os" + + "github.com/Open-CAS/open-cas-linux/json/ioctl" +) + +/** packages of JSON marshalable structs with packed CAS info for easy use in RESTful API */ + +type Cache_with_cores struct { + // default value of ptr is nil + Cache_info *ioctl.Kcas_cache_info `json:"Cache"` + Cores_info []*ioctl.Kcas_core_info `json:"Cores"` +} + +type Cache_list_pkg []Cache_with_cores + +/** Retriving caches and their cores info from CAS */ + +func List_caches() (Cache_list_pkg, error) { + var cache_list_pkg Cache_list_pkg + + /** retive list of caches id from ioclt */ + cache_id_list, ioctl_err := get_cache_id_list() + + /** early log and return in case of cache_list_id retrieve error + which is needed in further caches iteration for ioctl calls */ + if check_err(ioctl_err, ioctl_err.Error(), log_console) { + return cache_list_pkg, ioctl_err + } + + /** create array of caches with list of their cores */ + cache_id_list_len := len(cache_id_list) + cache_list_pkg = make([]Cache_with_cores, cache_id_list_len) + + for cache_idx, cache_id := range cache_id_list { + /** retrieve cache info */ + cache_info, ioctl_err := get_cache_info(cache_id) + + /** in case of get_cache_info error, log error and skip adding that cache and it's cores to package */ + if check_err(ioctl_err, ioctl_err.Error(), log_console) { + return cache_list_pkg, ioctl_err + } + + /** create array of cores for each cache */ + cores_id_list_len := len(cache_info.Attached_cores) + cache_list_pkg[cache_idx].Cores_info = make([]*ioctl.Kcas_core_info, cores_id_list_len) + + /** fill array of caches & their cores with retrieved cache info */ + cache_list_pkg[cache_idx].Cache_info = &cache_info + + for core_idx, core_id := range cache_info.Attached_cores { + /** retrieve core info for table of cores assigned to cache */ + core_info, ioctl_err := get_core_info(cache_id, core_id) + + /** in case of get_core_info error, log error and skip adding that core to package */ + if check_err(ioctl_err, ioctl_err.Error(), log_console) { + return cache_list_pkg, ioctl_err + } + + /** fill array of cores with retrieved core info */ + cache_list_pkg[cache_idx].Cores_info[core_idx] = &core_info + } + } + /** return array of caches with their cores list and no error */ + return cache_list_pkg, nil +} + +/** retriving CAS structures with ioctl syscalls and converting them into Go Marshalable structs */ + +/** retrieves C cache info struct with ioctl syscall and converts it into Golang cache info struct +also provides errno type error into standard golang error conversion */ +func get_cache_info(cache_id uint16) (ioctl.Kcas_cache_info, error) { + /** retrieve file descriptor of exported object cas_ctrl + and close with defer after completing all tasks */ + cas_ctrl, fd_err := os.Open(ioctl.Cas_ctrl_path) + if check_err(fd_err, "Cannot open device exclusively", log_console) { + return ioctl.Kcas_cache_info{}, fd_err + } + defer cas_ctrl.Close() + + C_cache_info, ioctl_err := ioctl.Ioctl_cache_info(cas_ctrl.Fd(), cache_id) + cache_info := ioctl.Conv_cache_info(&C_cache_info) + ioctl_err = errno_to_error(ioctl_err) + return cache_info, ioctl_err +} + +/** retrieves C core info struct with ioctl syscall and converts it into Golang core info struct +also provides errno type error into standard golang error conversion */ +func get_core_info(cache_id, core_id uint16) (ioctl.Kcas_core_info, error) { + /** retrieve file descriptor of exported object cas_ctrl + and close with defer after completing all tasks */ + cas_ctrl, fd_err := os.Open(ioctl.Cas_ctrl_path) + if check_err(fd_err, "Cannot open device exclusively", log_console) { + return ioctl.Kcas_core_info{}, fd_err + } + defer cas_ctrl.Close() + + C_core_info, ioctl_err := ioctl.Ioctl_core_info(cas_ctrl.Fd(), cache_id, core_id) + core_info := ioctl.Conv_core_info(&C_core_info) + ioctl_err = errno_to_error(ioctl_err) + return core_info, ioctl_err +} + +/** retrieves C io class struct with ioctl syscall and converts it into Golang io class struct +also provides errno type error into standard golang error conversion */ +func get_io_class(cache_id, io_class_id uint16) (ioctl.Kcas_io_class, error) { + /** retrieves file descriptor of exported object cas_ctrl + and close with defer after completing all tasks */ + cas_ctrl, fd_err := os.Open(ioctl.Cas_ctrl_path) + if check_err(fd_err, "Cannot open device exclusively", log_console) { + return ioctl.Kcas_io_class{}, fd_err + } + defer cas_ctrl.Close() + + C_io_class, ioctl_err := ioctl.Ioctl_io_class(cas_ctrl.Fd(), cache_id, io_class_id) + io_class := ioctl.Conv_io_class(&C_io_class) + ioctl_err = errno_to_error(ioctl_err) + return io_class, ioctl_err +} + +/** retrieves C statistics struct with ioctl syscall and converts it into Golang statistics struct +also provides errno type error into standard golang error conversion */ +func get_stats(cache_id, core_id, io_class_id uint16) (ioctl.Kcas_get_stats, error) { + /** retrieve file descriptor of exported object cas_ctrl + and close with defer after completing all tasks */ + cas_ctrl, fd_err := os.Open(ioctl.Cas_ctrl_path) + if check_err(fd_err, "Cannot open device exclusively", log_console) { + return ioctl.Kcas_get_stats{}, fd_err + } + defer cas_ctrl.Close() + + C_stats, ioctl_err := ioctl.Ioctl_get_kcas_stats(cas_ctrl.Fd(), cache_id, core_id, io_class_id) + stats := ioctl.Conv_stats(&C_stats) + ioctl_err = errno_to_error(ioctl_err) + return stats, ioctl_err +} + +/** retrieves C list of cache id's with ioctl syscall and converts it into Golang slice of cache id's +also provides errno type error into standard golang error conversion */ +func get_cache_id_list() ([]uint16, error) { + /** retrieves file descriptor of exported object cas_ctrl + and close with defer after completing all tasks */ + cas_ctrl, fd_err := os.Open(ioctl.Cas_ctrl_path) + if check_err(fd_err, "Cannot open device exclusively", log_console) { + return []uint16{}, fd_err + } + defer cas_ctrl.Close() + + C_cache_list, ioctl_err := ioctl.Ioctl_list_cache(cas_ctrl.Fd()) + cache_list := ioctl.Conv_cache_id_list(&C_cache_list) + ioctl_err = errno_to_error(ioctl_err) + return cache_list, ioctl_err +} + +/** validate file descriptor */ +func valid_fd(fd uintptr) bool { + return fd > 0 +} diff --git a/json/api/error.go b/json/api/error.go new file mode 100644 index 000000000..ab79773c7 --- /dev/null +++ b/json/api/error.go @@ -0,0 +1,62 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause + */ + +package api + +import "C" +import ( + "encoding/json" + "fmt" +) + +/** error checking and logging system */ +var err_sys Error_sys + +type Error_sys struct { + Log_error_list map[string]string `json:"error log"` + stack_error_msg []string +} + +/** Error_sys constructor */ +func NewError_sys(err_sys Error_sys) *Error_sys { + err_sys.Log_error_list = make(map[string]string) + return &err_sys +} + +/** marshal error log to json and transfer to stdout */ +func (err_sys *Error_sys) write_errors_to_console() { + Log_error_list_b, marshall_err := json.MarshalIndent(err_sys.Log_error_list, "", " ") + if check_err(marshall_err, "failed marshaling JSON error response", log_console) { + return + } + fmt.Println(string(Log_error_list_b)) +} + +/** check whether error occured and log it's occurance */ +func check_err(err error, msg string, log_err func(msg string)) bool { + if err != nil { + log_err(msg) + return true + } + return false +} + +/** logs errors to console */ +func log_console(msg string) { + if err_sys.Log_error_list == nil { + err_sys.Log_error_list = make(map[string]string) + err_sys.stack_error_msg = make([]string, 1) + err_sys.Log_error_list["error"] = msg + } + err_sys.stack_error_msg = append(err_sys.stack_error_msg, msg) +} + +/** error of errno type even if everything went write is not nil and have message "errno 0" so we check that */ +func errno_to_error(err error) error { + if err.Error() == "errno 0" { + return nil + } + return err +} diff --git a/json/api/json_api.go b/json/api/json_api.go new file mode 100644 index 000000000..544730b08 --- /dev/null +++ b/json/api/json_api.go @@ -0,0 +1,210 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause + */ + +package api + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/Jeffail/gabs" + "github.com/Open-CAS/open-cas-linux/json/ioctl" +) + +func Json_api() { + + var buffer interface{} + decode_header_err := json.NewDecoder(os.Stdin).Decode(&buffer) + if check_err(decode_header_err, "Invalid input parameter", log_console) { + err_sys.write_errors_to_console() + return + } + strB, _ := json.Marshal(buffer) + + jsonParsed, err := gabs.ParseJSON(strB) + if check_err(err, "Invalid input parameter", log_console) { + err_sys.write_errors_to_console() + return + } + + command, ok := jsonParsed.Path("command").Data().(string) + if !ok { + log_console("Invalid input parameter") + err_sys.write_errors_to_console() + return + } + + /** Comand interpretation section and retrieving json response */ + + if command == "opencas.cache.stats.get" { + // it appears as float64 is default format to read json in this package + cache_id, _ := jsonParsed.Search("params", "cache id").Data().(float64) + params := CreateParamsStatsCache(uint16(cache_id)) + json_get_stats_cache(*params) + } + + if command == "opencas.cache.core.stats.get" { + cache_id, _ := jsonParsed.Search("params", "cache id").Data().(float64) + core_id, _ := jsonParsed.Search("params", "core id").Data().(float64) + params := CreateParamsStatsCore(uint16(cache_id), uint16(core_id)) + json_get_stats_core(*params) + } + + if command == "opencas.cache.ioclass.stats.get" { + cache_id, _ := jsonParsed.Search("params", "cache id").Data().(float64) + io_class, _ := jsonParsed.Search("params", "io class").Data().(float64) + params := CreateParamsStatsIoClass(uint16(cache_id), uint16(io_class)) + json_get_stats_io_class(*params) + } + + if command == "opencas.cache.core.ioclass.stats.get" { + cache_id, _ := jsonParsed.Search("params", "cache id").Data().(float64) + core_id, _ := jsonParsed.Search("params", "core id").Data().(float64) + io_class, _ := jsonParsed.Search("params", "io class").Data().(float64) + params := CreateParamsStats(uint16(cache_id), uint16(core_id), uint16(io_class)) + json_get_stats(*params) + } + + if command == "opencas.cache_list.get" { + json_list_caches() + } + + if command == "opencas.cache.info.get" { + cache_id, _ := jsonParsed.Search("params", "cache id").Data().(float64) + params := CreateParamsCacheInfo(uint16(cache_id)) + json_get_cache_info(*params) + } + + if command == "opencas.cache.core.info.get" { + cache_id, _ := jsonParsed.Search("params", "cache id").Data().(float64) + core_id, _ := jsonParsed.Search("params", "core id").Data().(float64) + params := CreateParamsCoreInfo(uint16(cache_id), uint16(core_id)) + json_get_core_info(*params) + } + + if command == "opencas.cache.ioclass.info.get" { + cache_id, _ := jsonParsed.Search("params", "cache id").Data().(float64) + io_class, _ := jsonParsed.Search("params", "io class").Data().(float64) + params := CreateParamsIoclassInfo(uint16(cache_id), uint16(io_class)) + json_get_io_class_info(*params) + } + + /** if some errors occured - error log is not empty write errors list */ + if len(err_sys.Log_error_list) != 0 { + err_sys.write_errors_to_console() + } + +} + +/** json get stats with different levels */ + +func json_get_stats(request Params_stats) { + stats_pkg, get_stats_err := get_stats(request.Cache_id, request.Core_id, request.Io_class) + if check_err(get_stats_err, get_stats_err.Error(), log_console) { + return + } + + stats_pkg_b, marshall_err := json.MarshalIndent(stats_pkg, "", " ") + if check_err(marshall_err, "failed marshaling JSON response", log_console) { + return + } + fmt.Println(string(stats_pkg_b)) +} + +func json_get_stats_cache(request Params_stats_cache) { + stats_pkg, get_stats_err := get_stats(request.Cache_id, ioctl.Invalid_core_id, ioctl.Invalid_io_class) + if check_err(get_stats_err, get_stats_err.Error(), log_console) { + return + } + + stats_pkg_b, marshall_err := json.MarshalIndent(stats_pkg, "", " ") + if check_err(marshall_err, "failed marshaling JSON response", log_console) { + return + } + fmt.Println(string(stats_pkg_b)) +} + +func json_get_stats_core(request Params_stats_core) { + stats_pkg, get_stats_err := get_stats(request.Cache_id, request.Core_id, ioctl.Invalid_io_class) + if check_err(get_stats_err, get_stats_err.Error(), log_console) { + return + } + + stats_pkg_b, marshall_err := json.MarshalIndent(stats_pkg, "", " ") + if check_err(marshall_err, "failed marshaling JSON response", log_console) { + return + } + fmt.Println(string(stats_pkg_b)) +} + +func json_get_stats_io_class(request Params_stats_io_class) { + stats_pkg, get_stats_err := get_stats(request.Cache_id, ioctl.Invalid_core_id, request.Io_class) + if check_err(get_stats_err, get_stats_err.Error(), log_console) { + return + } + + stats_pkg_b, marshall_err := json.MarshalIndent(stats_pkg, "", " ") + if check_err(marshall_err, "failed marshaling JSON response", log_console) { + return + } + fmt.Println(string(stats_pkg_b)) +} + +/** json get cache_list */ + +func json_list_caches() { + cache_list_pkg, list_caches_err := List_caches() + if check_err(list_caches_err, list_caches_err.Error(), log_console) { + return + } + + cache_list_pkg_b, marshall_err := json.MarshalIndent(cache_list_pkg, "", " ") + if check_err(marshall_err, "failed marshaling JSON response", log_console) { + return + } + fmt.Println(string(cache_list_pkg_b)) +} + +/** json get CAS with different levels */ + +func json_get_cache_info(request Params_cache_info) { + cache_info, cache_info_err := get_cache_info(request.Cache_id) + if check_err(cache_info_err, cache_info_err.Error(), log_console) { + return + } + + cache_info_b, marshall_err := json.MarshalIndent(cache_info, "", " ") + if check_err(marshall_err, "failed marshaling JSON response", log_console) { + return + } + fmt.Println(string(cache_info_b)) +} + +func json_get_core_info(request Params_core_info) { + core_info, core_info_err := get_core_info(request.Cache_id, request.Core_id) + if check_err(core_info_err, core_info_err.Error(), log_console) { + return + } + + core_info_b, marshall_err := json.MarshalIndent(core_info, "", " ") + if check_err(marshall_err, "failed marshaling JSON response", log_console) { + return + } + fmt.Println(string(core_info_b)) +} + +func json_get_io_class_info(request Params_io_class_info) { + io_class_info, io_class_info_err := get_io_class(request.Cache_id, request.Io_class) + if check_err(io_class_info_err, io_class_info_err.Error(), log_console) { + return + } + + io_class_info_b, marshall_err := json.MarshalIndent(io_class_info, "", " ") + if check_err(marshall_err, "failed marshaling JSON response", log_console) { + return + } + fmt.Println(string(io_class_info_b)) +} diff --git a/json/api/request.go b/json/api/request.go new file mode 100644 index 000000000..e2146fca9 --- /dev/null +++ b/json/api/request.go @@ -0,0 +1,206 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause + */ + +package api + +import ( + "encoding/json" + "errors" + "os" +) + +/** Request structures */ + +type Request struct { + Command string `json:"command"` + Rparams Params `json:"params"` +} + +/** parameters to varius request types*/ + +type Params interface { + Read() +} + +/** most specyfic stats for concrete core with specyfied io class*/ +type Params_stats struct { + Cache_id uint16 `json:"cache id"` // obligatory + Core_id uint16 `json:"core id"` // obligatory + Io_class uint16 `json:"io class"` // obligatory +} + +/** cache level stats */ +type Params_stats_cache struct { + Cache_id uint16 `json:"cache id"` // obligatory +} + +/** core level stats */ +type Params_stats_core struct { + Cache_id uint16 `json:"cache id"` // obligatory + Core_id uint16 `json:"core id"` // obligatory + +} + +/** io class level stats */ +type Params_stats_io_class struct { + Cache_id uint16 `json:"cache id"` // obligatory + Io_class uint16 `json:"io class"` // obligatory +} + +type Params_list_caches struct { +} + +type Params_cache_info struct { + Cache_id uint16 `json:"cache id"` // obligatory +} + +type Params_core_info struct { + Cache_id uint16 `json:"cache id"` // obligatory + Core_id uint16 `json:"core id"` // obligatory +} + +type Params_io_class_info struct { + Cache_id uint16 `json:"cache id"` // obligatory + Io_class uint16 `json:"io class"` // obligatory +} + +/** Request parameters constructors */ + +func CreateParamsStats(cache_id, core_id, io_class_id uint16) *Params_stats { + return &Params_stats{ + Cache_id: cache_id, + Core_id: core_id, + Io_class: io_class_id, + } +} + +func CreateParamsStatsCache(cache_id uint16) *Params_stats_cache { + return &Params_stats_cache{ + Cache_id: cache_id, + } +} + +func CreateParamsStatsCore(cache_id, core_id uint16) *Params_stats_core { + return &Params_stats_core{ + Cache_id: cache_id, + Core_id: core_id, + } +} + +func CreateParamsStatsIoClass(cache_id, io_class_id uint16) *Params_stats_io_class { + return &Params_stats_io_class{ + Cache_id: cache_id, + Io_class: io_class_id, + } +} +func CreateParamsCacheList() *Params_list_caches { + return &Params_list_caches{} +} + +func CreateParamsCacheInfo(cache_id uint16) *Params_cache_info { + return &Params_cache_info{ + Cache_id: cache_id, + } +} + +func CreateParamsCoreInfo(cache_id, core_id uint16) *Params_core_info { + return &Params_core_info{ + Cache_id: cache_id, + Core_id: core_id, + } +} +func CreateParamsIoclassInfo(cache_id, io_class_id uint16) *Params_io_class_info { + return &Params_io_class_info{ + Cache_id: cache_id, + Io_class: io_class_id, + } +} + +/** factory design pattern for creating customized in header command parameters */ +func NewRequest(command string) (Params, error) { + if command == "opencas.cache.stats.get" { + return new(Params_stats_cache), nil + } + if command == "opencas.cache.core.stats.get" { + return new(Params_core_info), nil + } + if command == "opencas.cache.ioclass.stats.get" { + return new(Params_io_class_info), nil + } + if command == "opencas.cache.core.ioclass.stats.get" { + return new(Params_stats), nil + } + if command == "opencas.cache_list.get" { + return new(Params_list_caches), nil + } + if command == "opencas.cache.info.get" { + return new(Params_cache_info), nil + } + if command == "opencas.core.info.get" { + return new(Params_core_info), nil + } + if command == "opencas.ioclass.info.get" { + return new(Params_io_class_info), nil + } + return nil, errors.New("Invalid input parameter") +} + +/** read JSON from stdin functions */ + +func (request *Params_stats) Read() { + decode_request_err := json.NewDecoder(os.Stdin).Decode(request) + if decode_request_err != nil { + log_console("Invalid input parameter") + } +} + +func (request *Params_stats_cache) Read() { + decode_request_err := json.NewDecoder(os.Stdin).Decode(request) + if decode_request_err != nil { + log_console("Invalid input parameter") + } +} + +func (request *Params_stats_core) Read() { + decode_request_err := json.NewDecoder(os.Stdin).Decode(request) + if decode_request_err != nil { + log_console("Invalid input parameter") + } +} + +func (request *Params_stats_io_class) Read() { + decode_request_err := json.NewDecoder(os.Stdin).Decode(request) + if decode_request_err != nil { + log_console("Invalid input parameter") + } +} + +func (request *Params_list_caches) Read() { + decode_request_err := json.NewDecoder(os.Stdin).Decode(request) + if decode_request_err != nil { + log_console("Invalid input parameter") + } +} + +func (request *Params_cache_info) Read() { + decode_request_err := json.NewDecoder(os.Stdin).Decode(request) + if decode_request_err != nil { + log_console("Invalid input parameter") + } +} + +func (request *Params_core_info) Read() { + decode_request_err := json.NewDecoder(os.Stdin).Decode(request) + if decode_request_err != nil { + log_console("Invalid input parameter") + } +} + +func (request *Params_io_class_info) Read() { + decode_request_err := json.NewDecoder(os.Stdin).Decode(request) + if decode_request_err != nil { + log_console("Invalid input parameter") + } +} diff --git a/json/go.mod b/json/go.mod index 8228b8b67..51ab08f57 100644 --- a/json/go.mod +++ b/json/go.mod @@ -1,3 +1,8 @@ -module example.com/json +module github.com/Open-CAS/open-cas-linux/json go 1.13 + +require ( + github.com/Jeffail/gabs v1.4.0 + github.com/gin-gonic/gin v1.7.4 +) diff --git a/json/ioctl/ioctl.go b/json/ioctl/ioctl.go index f619bd3db..ae1a97310 100644 --- a/json/ioctl/ioctl.go +++ b/json/ioctl/ioctl.go @@ -1,77 +1,96 @@ /* * Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear -*/ +* SPDX-License-Identifier: BSD-3-Clause + */ package ioctl -// #cgo CFLAGS: -I./../../modules/include/ -I/ocf_env_headers.h -/* -#include -*/ +// #cgo CFLAGS: -I./../../modules/include/ -I./../../casadm +// #include +// #include <./../../casadm/extended_err_msg.c> +// #include import "C" import ( - "log" - "os" + "errors" "syscall" "unsafe" ) -/** Get CAS exported device file descriptor **/ +/** CAS control device path **/ -func Read_fd() uintptr { - file, err := os.Open("/dev/cas_ctrl") - if err != nil { - log.Fatal(err) - } - return file.Fd() -} +var Cas_ctrl_path string = "/dev/cas_ctrl" /** Use syscall ioctl to get get_kcas_stats structs **/ +// returned values of syscall r1, r2 skipped (register1 value, register2 value, Errno error) -func Ioctl_get_kcas_stats(fd uintptr, cache_id, core_id, part_id uint16) C.struct_kcas_get_stats { +func Ioctl_get_kcas_stats(fd uintptr, cache_id, core_id, part_id uint16) (C.struct_kcas_get_stats, error) { C_kstats := C.struct_kcas_get_stats{cache_id: C.ushort(cache_id), core_id: C.ushort(core_id), part_id: C.ushort(part_id)} - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_GET_STATS, + _, _, ioctl_err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_GET_STATS, uintptr(unsafe.Pointer(&C_kstats))) - if err != 0 { - log.Fatal(err) + if C_kstats.ext_err_code == 0 { + ext_err := ext_err_code_to_string(int(C_kstats.ext_err_code)) + return C_kstats, ext_err } - return C_kstats + return C_kstats, ioctl_err } /** Use syscall ioctl to get cache info struct **/ -func Ioctl_cache_info(fd uintptr, cache_id uint16) C.struct_kcas_cache_info { +func Ioctl_cache_info(fd uintptr, cache_id uint16) (C.struct_kcas_cache_info, error) { C_kcache_info := C.struct_kcas_cache_info{cache_id: C.ushort(cache_id)} - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_CACHE_INFO, + _, _, ioctl_err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_CACHE_INFO, uintptr(unsafe.Pointer(&C_kcache_info))) - if err != 0 { - log.Fatal(err) + if C_kcache_info.ext_err_code == 0 { + ext_err := ext_err_code_to_string(int(C_kcache_info.ext_err_code)) + return C_kcache_info, ext_err } - return C_kcache_info + return C_kcache_info, ioctl_err } /** Use syscall ioctl to get core info struct **/ -func Ioctl_core_info(fd uintptr, cache_id, core_id uint16) C.struct_kcas_core_info { +func Ioctl_core_info(fd uintptr, cache_id, core_id uint16) (C.struct_kcas_core_info, error) { C_kcore_info := C.struct_kcas_core_info{cache_id: C.ushort(cache_id), core_id: C.ushort(core_id)} - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_CORE_INFO, + _, _, ioctl_err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_CORE_INFO, uintptr(unsafe.Pointer(&C_kcore_info))) - if err != 0 { - log.Fatal(err) + if C_kcore_info.ext_err_code == 0 { + ext_err := ext_err_code_to_string(int(C_kcore_info.ext_err_code)) + return C_kcore_info, ext_err } - return C_kcore_info + return C_kcore_info, ioctl_err } /** Use syscall ioctl to get io class struct **/ -func Ioctl_io_class(fd uintptr, cache_id, class_id uint16) C.struct_kcas_io_class { +func Ioctl_io_class(fd uintptr, cache_id, class_id uint16) (C.struct_kcas_io_class, error) { C_kio_class := C.struct_kcas_io_class{cache_id: C.ushort(cache_id), class_id: C.uint(class_id)} - _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_PARTITION_INFO, + _, _, ioctl_err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_PARTITION_INFO, uintptr(unsafe.Pointer(&C_kio_class))) - if err != 0 { - log.Fatal(err) + if C_kio_class.ext_err_code == 0 { + ext_err := ext_err_code_to_string(int(C_kio_class.ext_err_code)) + return C_kio_class, ext_err + } + return C_kio_class, ioctl_err +} + +/** Use syscall ioctl to get list of cache ids **/ + +func Ioctl_list_cache(fd uintptr) (C.struct_kcas_cache_list, error) { + C_kcache_list := C.struct_kcas_cache_list{} + _, _, ioctl_err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.KCAS_IOCTL_LIST_CACHE, + uintptr(unsafe.Pointer(&C_kcache_list))) + if C_kcache_list.ext_err_code == 0 { + ext_err := ext_err_code_to_string(int(C_kcache_list.ext_err_code)) + return C_kcache_list, ext_err + } + return C_kcache_list, ioctl_err +} + +/** maping CAS ext_err_code to Go string error messages */ +func ext_err_code_to_string(ext_err_code int) error { + if ext_err_code == 0 { + return nil } - return C_kio_class + return errors.New(C.GoString(C.cas_strerr(C.int(ext_err_code)))) } diff --git a/json/ioctl/json_structs.go b/json/ioctl/json_structs.go index e77514e6d..b5c0696d9 100644 --- a/json/ioctl/json_structs.go +++ b/json/ioctl/json_structs.go @@ -1,21 +1,16 @@ /* * Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear -*/ +* SPDX-License-Identifier: BSD-3-Clause + */ package ioctl -/* -#include -*/ -import "C" - -/** IOCTL_KCAS_GET_STATS structs **/ +/** IOCTL_KCAS_GET_STATS structs */ type Kcas_get_stats struct { Cache_id uint16 `json:"Cache id"` Core_id uint16 `json:"Core id"` - IO_class_id uint16 `json:"Part id"` + IO_class uint16 `json:"IO class"` Usage Ocf_stats_usage `json:"Usage"` Req Ocf_stats_requests `json:"Requests"` Blocks Ocf_stats_blocks `json:"Blocks"` @@ -33,7 +28,7 @@ type Ocf_stats_usage struct { type Ocf_stats_requests struct { Rd_hits Ocf_stat `json:"Read hits"` Rd_partial_misses Ocf_stat `json:"Read partial misses"` - Rd_full_misses Ocf_stat `json:"Read full misses"` + Rd_full_misses Ocf_stat `json:"Read full misses"` Rd_total Ocf_stat `json:"Read total"` Wr_hits Ocf_stat `json:"Write hits"` Wr_partial_misses Ocf_stat `json:"Write partial misses"` @@ -48,13 +43,13 @@ type Ocf_stats_requests struct { type Ocf_stats_blocks struct { Core_volume_rd Ocf_stat `json:"Reads from core(s)"` Core_volume_wr Ocf_stat `json:"Writes from core(s)"` - Core_volume_total Ocf_stat `json:"Total to/from core(s)"` + Core_volume_total Ocf_stat `json:"Total from/to core(s)"` Cache_volume_rd Ocf_stat `json:"Reads from cache(s)"` Cache_volume_wr Ocf_stat `json:"Writes from cache(s)"` - Cache_volume_total Ocf_stat `json:"Total to/from cache(s)"` + Cache_volume_total Ocf_stat `json:"Total from/to cache(s)"` Volume_rd Ocf_stat `json:"Reads from exported object(s)"` Volume_wr Ocf_stat `json:"Writes from exported object(s)"` - Volume_total Ocf_stat `json:"Total to/from exported object(s)"` + Volume_total Ocf_stat `json:"Total from/to exported object(s)"` } type Ocf_stats_errors struct { @@ -68,8 +63,8 @@ type Ocf_stats_errors struct { } type Ocf_stat struct { - Value uint64 `json:"page"` - Fraction uint64 `json:"fraction"` + Value uint64 `json:"Page"` + Fraction uint64 `json:"Fraction"` } /** IOCTL_CACHE_INFO structs **/ @@ -77,15 +72,14 @@ type Ocf_stat struct { type Kcas_cache_info struct { Cache_id uint16 `json:"Cache id"` Cache_path_name string `json:"Cache device"` - Core_id []uint16 `json:"Core(s) id(s)"` + Attached_cores []uint16 `json:"Core(s) id(s)"` Info Ocf_cache_info `json:"Cache details"` - Metadata_mode string `json:"Metadata mode"` ext_err_code int } type Ocf_cache_info struct { Attached bool `json:"Attached"` - Volume_type uint8 `json:"Volume type"` // mby enum -> convert to str? + volume_type uint8 `json:"Volume type"` // mby enum -> convert to str? State string `json:"Status"` Size uint32 `json:"Size [cache lines]"` Inactive_cores Core_stats `json:"Inactive cores"` @@ -96,7 +90,7 @@ type Ocf_cache_info struct { Cache_mode string `json:"Cache mode"` Fallback_pt Fallback_pt_stats `json:"Pass-Trough fallback statistics"` Cleaning_policy string `json:"Cleaning policy"` - Promotion_policy string `json:"Promotion policy"` + Promotion_policy string `json:"Promotion policy"` Cache_line_size uint64 `json:"Cache line size [KiB]"` Flushed uint32 `json:"Flushed blocks"` Core_count uint32 `json:"Core count"` @@ -118,9 +112,9 @@ type Fallback_pt_stats struct { /** IOCTL_CORE_INFO structs **/ type Kcas_core_info struct { - Core_path_name string `json:"Core path"` - Cache_id uint16 `json:"Cache id"` + cache_id uint16 `json:"Cache id"` Core_id uint16 `json:"Core id"` + Core_path_name string `json:"Core path"` Info Ocf_core_info `json:"Core details"` State string `json:"State"` ext_err_code int @@ -139,9 +133,9 @@ type Ocf_core_info struct { /** IOCTL_KCAS_IO_CLASS structs **/ type Kcas_io_class struct { - Cache_id uint16 `json:"Cache id"` + cache_id uint16 `json:"Cache id"` Class_id uint32 `json:"Class id"` - Info Ocf_io_class_info "IO class details" + Info Ocf_io_class_info `json:"IO class details"` ext_err_code int } @@ -150,7 +144,7 @@ type Ocf_io_class_info struct { Cache_mode string `json:"Cache mode"` Priority int16 `json:"Priority"` Curr_size uint32 `json:"Current size [cache line]"` - Min_size int32 `json:"Min size [cache line]"` - Max_size int32 `json:"Max size [cache line]"` + Min_size int32 `json:"Min size [%]"` + Max_size int32 `json:"Max size [%]"` Cleaning_policy string `json:"Cleaning policy"` } diff --git a/json/ioctl/marshal.go b/json/ioctl/marshal.go deleted file mode 100644 index 026a6c81d..000000000 --- a/json/ioctl/marshal.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -* Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear -*/ - -package ioctl - -import ( - "encoding/json" - "fmt" -) - -/** Marshal GoStructs into JSON **/ - -func Marshal_kcas_stats(kstats Kcas_get_stats) ([]byte, error) { - kstats_b, err := json.MarshalIndent(kstats, "", " ") - fmt.Println("Statistics: ", string(kstats_b), "\n") - return kstats_b, err -} - -func Marshal_kcache_info(kcache_info Kcas_cache_info) ([]byte, error) { - kcache_info_b, err := json.MarshalIndent(kcache_info, "", " ") - fmt.Println("Cache info: ", string(kcache_info_b), "\n") - return kcache_info_b, err -} - -func Marshal_kcore_info(kcore_info Kcas_core_info) ([]byte, error) { - kcore_info_b, err := json.MarshalIndent(kcore_info, "", " ") - fmt.Println("Core info: ", string(kcore_info_b), "\n") - return kcore_info_b, err -} - -func Marshal_kio_class(kio_class Kcas_io_class) ([]byte, error) { - kio_class_b, err := json.MarshalIndent(kio_class, "", " ") - fmt.Println("IO class", string(kio_class_b), "\n") - return kio_class_b, err -} diff --git a/json/ioctl/ocf_env_headers.h b/json/ioctl/ocf_env_headers.h deleted file mode 100644 index c26519cd5..000000000 --- a/json/ioctl/ocf_env_headers.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -* Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear -*/ - -#ifndef __OCF_ENV_HEADERS_H__ -#define __OCF_ENV_HEADERS_H__ - -#include -#include -#include - -/* TODO: Move prefix printing to context logger. */ -#define OCF_LOGO "CAS" -#define OCF_PREFIX_SHORT "[" OCF_LOGO "] " -#define OCF_PREFIX_LONG "Cache Acceleration Software Linux" - -#define OCF_VERSION_MAIN CAS_VERSION_MAIN -#define OCF_VERSION_MAJOR CAS_VERSION_MAJOR -#define OCF_VERSION_MINOR CAS_VERSION_MINOR - -#endif /* __OCF_ENV_HEADERS_H__ */ diff --git a/json/ioctl/ocf_types.go b/json/ioctl/ocf_types.go index d662c0ce8..80e6a8ed1 100644 --- a/json/ioctl/ocf_types.go +++ b/json/ioctl/ocf_types.go @@ -1,14 +1,25 @@ /* * Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear +* SPDX-License-Identifier: BSD-3-Clause */ package ioctl -var ocf_cache_state_str = [...]string{"running", "stopping", "initializing", "incomplete", "max"} -var ocf_core_state_str = [...]string{"active", "inactive", "max"} -var ocf_cache_mode_str = [...]string{"wt", "wb", "wa", "pt", "wi", "wo", "max"} -var ocf_cleaning_str = [...]string{"nop", "alru", "acp", "max"} -var metadata_mode_str = [...]string{"invalid", "normal", "atomic"} -var ocf_seq_cutoff_policy_str = [...]string{"always", "full", "never", "max"} -var ocf_promotion_str = [...]string{"always", "nhit", "max"} +// #cgo CFLAGS: -I./../../modules/include/ +// #include +import "C" + +const ( + invalid_cache_id = C.OCF_CACHE_ID_INVALID + Invalid_core_id = C.OCF_CORE_ID_INVALID + Invalid_io_class = C.OCF_IO_CLASS_INVALID +) + +/** ocf enum -> string mapping **/ + +var ocf_cache_state_str = []string{"running", "stopping", "initializing", "incomplete", "passive", "max"} +var ocf_core_state_str = []string{"active", "inactive", "max"} +var ocf_cache_mode_str = []string{"wt", "wb", "wa", "pt", "wi", "wo", "max"} +var ocf_cleaning_policy_str = []string{"nop", "alru", "acp", "max"} +var ocf_seq_cutoff_policy_str = []string{"always", "full", "never", "max"} +var ocf_promotion_str = []string{"always", "nhit", "max"} diff --git a/json/ioctl/structs_conv.go b/json/ioctl/structs_conv.go new file mode 100644 index 000000000..37d7f67f9 --- /dev/null +++ b/json/ioctl/structs_conv.go @@ -0,0 +1,255 @@ +/* +* Copyright(c) 2012-2021 Intel Corporation +* SPDX-License-Identifier: BSD-3-Clause + */ + +package ioctl + +// #cgo CFLAGS: -I./../../modules/include/ +// #include +import "C" +import ( + "unsafe" +) + +/** kcas_get_stats struct conversion C to Go **/ + +func Conv_stats(C_kstats *C.struct_kcas_get_stats) Kcas_get_stats { + res := Kcas_get_stats{ + Cache_id: uint16(C_kstats.cache_id), + Core_id: uint16(C_kstats.core_id), + IO_class: uint16(C_kstats.part_id), + Usage: Ocf_stats_usage{ + Occupancy: Ocf_stat{Value: uint64(C_kstats.usage.occupancy.value), + Fraction: uint64(C_kstats.usage.occupancy.fraction)}, + Free: Ocf_stat{Value: uint64(C_kstats.usage.free.value), + Fraction: uint64(C_kstats.usage.free.fraction)}, + Clean: Ocf_stat{Value: uint64(C_kstats.usage.clean.value), + Fraction: uint64(C_kstats.usage.clean.fraction)}, + Dirty: Ocf_stat{Value: uint64(C_kstats.usage.dirty.value), + Fraction: uint64(C_kstats.usage.dirty.fraction)}, + }, + Req: Ocf_stats_requests{ + Rd_hits: Ocf_stat{Value: uint64(C_kstats.req.rd_hits.value), + Fraction: uint64(C_kstats.req.rd_hits.fraction)}, + Rd_partial_misses: Ocf_stat{Value: uint64(C_kstats.req.rd_partial_misses.value), + Fraction: uint64(C_kstats.req.rd_partial_misses.fraction)}, + Rd_full_misses: Ocf_stat{Value: uint64(C_kstats.req.rd_full_misses.value), + Fraction: uint64(C_kstats.req.rd_full_misses.fraction)}, + Rd_total: Ocf_stat{Value: uint64(C_kstats.req.rd_total.value), + Fraction: uint64(C_kstats.req.rd_total.fraction)}, + Wr_hits: Ocf_stat{Value: uint64(C_kstats.req.wr_hits.value), + Fraction: uint64(C_kstats.req.wr_hits.fraction)}, + Wr_partial_misses: Ocf_stat{Value: uint64(C_kstats.req.wr_partial_misses.value), + Fraction: uint64(C_kstats.req.wr_partial_misses.fraction)}, + Wr_full_misses: Ocf_stat{Value: uint64(C_kstats.req.wr_full_misses.value), + Fraction: uint64(C_kstats.req.wr_full_misses.fraction)}, + Wr_total: Ocf_stat{Value: uint64(C_kstats.req.wr_total.value), + Fraction: uint64(C_kstats.req.wr_total.fraction)}, + Rd_pt: Ocf_stat{Value: uint64(C_kstats.req.rd_pt.value), + Fraction: uint64(C_kstats.req.rd_pt.fraction)}, + Wr_pt: Ocf_stat{Value: uint64(C_kstats.req.wr_pt.value), + Fraction: uint64(C_kstats.req.wr_pt.fraction)}, + Serviced: Ocf_stat{Value: uint64(C_kstats.req.serviced.value), + Fraction: uint64(C_kstats.req.serviced.fraction)}, + Total: Ocf_stat{Value: uint64(C_kstats.req.total.value), + Fraction: uint64(C_kstats.req.total.fraction)}, + }, + Blocks: Ocf_stats_blocks{ + Core_volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_rd.value), + Fraction: uint64(C_kstats.blocks.core_volume_rd.fraction)}, + Core_volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_wr.value), + Fraction: uint64(C_kstats.blocks.core_volume_wr.fraction)}, + Core_volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_total.value), + Fraction: uint64(C_kstats.blocks.core_volume_total.fraction)}, + Cache_volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_rd.value), + Fraction: uint64(C_kstats.blocks.cache_volume_rd.fraction)}, + Cache_volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_wr.value), + Fraction: uint64(C_kstats.blocks.cache_volume_wr.fraction)}, + Cache_volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_total.value), + Fraction: uint64(C_kstats.blocks.cache_volume_total.fraction)}, + Volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.volume_rd.value), + Fraction: uint64(C_kstats.blocks.volume_rd.fraction)}, + Volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.volume_wr.value), + Fraction: uint64(C_kstats.blocks.volume_wr.fraction)}, + Volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.volume_total.value), + Fraction: uint64(C_kstats.blocks.volume_total.fraction)}, + }, + Errors: Ocf_stats_errors{ + Core_volume_rd: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_rd.value), + Fraction: uint64(C_kstats.errors.core_volume_rd.fraction)}, + Core_volume_wr: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_wr.value), + Fraction: uint64(C_kstats.errors.core_volume_wr.fraction)}, + Core_volume_total: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_total.value), + Fraction: uint64(C_kstats.errors.core_volume_total.fraction)}, + Cache_volume_rd: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_rd.value), + Fraction: uint64(C_kstats.errors.cache_volume_rd.fraction)}, + Cache_volume_wr: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_wr.value), + Fraction: uint64(C_kstats.errors.cache_volume_wr.fraction)}, + Cache_volume_total: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_total.value), + Fraction: uint64(C_kstats.errors.cache_volume_total.fraction)}, + Total: Ocf_stat{Value: uint64(C_kstats.errors.total.value), + Fraction: uint64(C_kstats.errors.total.fraction)}, + }, + ext_err_code: int(C_kstats.ext_err_code), + } + return res +} + +/** cache_info struct conversion C to Go **/ + +func Conv_cache_info(C_kcache_info *C.struct_kcas_cache_info) Kcas_cache_info { + + /** fields with no standard initialization rquired */ + attached_cores := get_attached_cores(C_kcache_info) + state := valid_enum_idx(int(C_kcache_info.info.state), ocf_cache_state_str) + cache_mode := valid_enum_idx(int(C_kcache_info.info.cache_mode), ocf_cache_mode_str) + cleaning_policy := valid_enum_idx(int(C_kcache_info.info.cleaning_policy), ocf_cleaning_policy_str) + promotion_policy := valid_enum_idx(int(C_kcache_info.info.promotion_policy), ocf_promotion_str) + + res := Kcas_cache_info{ + Cache_id: uint16(C_kcache_info.cache_id), + Cache_path_name: C.GoString(&C_kcache_info.cache_path_name[0]), + Attached_cores: attached_cores, + Info: Ocf_cache_info{ + Attached: bool(C_kcache_info.info.attached), + volume_type: uint8(C_kcache_info.info.volume_type), + State: state, + Size: uint32(C_kcache_info.info.size), + Inactive_cores: Core_stats{ + Occupancy: Ocf_stat{Value: uint64(C_kcache_info.info.inactive.occupancy.value), + Fraction: uint64(C_kcache_info.info.inactive.occupancy.fraction)}, + Clean: Ocf_stat{Value: uint64(C_kcache_info.info.inactive.clean.value), + Fraction: uint64(C_kcache_info.info.inactive.clean.fraction)}, + Dirty: Ocf_stat{Value: uint64(C_kcache_info.info.inactive.dirty.value), + Fraction: uint64(C_kcache_info.info.inactive.dirty.fraction)}, + }, + Occupancy: uint32(C_kcache_info.info.occupancy), + Dirty: uint32(C_kcache_info.info.dirty), + Dirty_for: uint64(C_kcache_info.info.dirty_for), + Dirty_initial: uint32(C_kcache_info.info.dirty_initial), + Cache_mode: cache_mode, + Fallback_pt: Fallback_pt_stats{ + Error_counter: int(C_kcache_info.info.fallback_pt.error_counter), + Status: bool(C_kcache_info.info.fallback_pt.status), + }, + Cleaning_policy: cleaning_policy, + Promotion_policy: promotion_policy, + Cache_line_size: uint64(C_kcache_info.info.cache_line_size), + Flushed: uint32(C_kcache_info.info.flushed), + Core_count: uint32(C_kcache_info.info.core_count), + Metadata_footprint: uint64(C_kcache_info.info.metadata_footprint), + Metadata_end_offset: uint64(C_kcache_info.info.metadata_end_offset), + }, + ext_err_code: int(C_kcache_info.ext_err_code), + } + return res +} + +/** core_info struct conversion C to Go **/ + +func Conv_core_info(C_kcore_info *C.struct_kcas_core_info) Kcas_core_info { + + /** fields with no standard initialization rquired */ + seq_cutoff_policy := valid_enum_idx(int(C_kcore_info.info.seq_cutoff_policy), ocf_seq_cutoff_policy_str) + state := valid_enum_idx(int(C_kcore_info.state), ocf_core_state_str) + + res := Kcas_core_info{ + Core_path_name: C.GoString(&C_kcore_info.core_path_name[0]), + cache_id: uint16(C_kcore_info.cache_id), + Core_id: uint16(C_kcore_info.core_id), + Info: Ocf_core_info{ + Core_size: uint64(C_kcore_info.info.core_size), + Core_size_bytes: uint64(C_kcore_info.info.core_size_bytes), + Flushed: uint32(C_kcore_info.info.flushed), + Dirty: uint32(C_kcore_info.info.dirty), + Dirty_for: uint64(C_kcore_info.info.dirty_for), + Seq_cutoff_threshold: uint32(C_kcore_info.info.seq_cutoff_threshold), + Seq_cutoff_policy: seq_cutoff_policy, + }, + State: state, + ext_err_code: int(C_kcore_info.ext_err_code), + } + return res +} + +/** io_class struct conversion C to Go **/ + +func Conv_io_class(C_kio_class *C.struct_kcas_io_class) Kcas_io_class { + + /** fields with no standard initialization rquired */ + cache_mode := valid_enum_idx(int(C_kio_class.info.cache_mode), ocf_cache_mode_str) + cleaning_policy := valid_enum_idx(int(C_kio_class.info.cleaning_policy_type), ocf_cleaning_policy_str) + + res := Kcas_io_class{ + cache_id: uint16(C_kio_class.cache_id), + Class_id: uint32(C_kio_class.class_id), + Info: Ocf_io_class_info{ + Name: C.GoString(&C_kio_class.info.name[0]), + Cache_mode: cache_mode, + Priority: int16(C_kio_class.info.priority), + Curr_size: uint32(C_kio_class.info.curr_size), + Min_size: int32(C_kio_class.info.min_size), + Max_size: int32(C_kio_class.info.max_size), + Cleaning_policy: cleaning_policy, + }, + ext_err_code: int(C_kio_class.ext_err_code), + } + return res +} + +// converts C type uint16[CACHE_LIST_ID_LIMIT] to Go slice []uint16 +// picks only valid caches and appends to result slice +func Conv_cache_id_list(C_kcache_list *C.struct_kcas_cache_list) []uint16 { + var valid_cache_ids []uint16 + cache_id_list := (*[C.CACHE_LIST_ID_LIMIT]uint16)(unsafe.Pointer(&C_kcache_list.cache_id_tab[0]))[:C.CACHE_LIST_ID_LIMIT:C.CACHE_LIST_ID_LIMIT] + + // Go in range for syntax - for index, value := range collection {} + for _, cache_id := range cache_id_list { + if valid_cache_id(cache_id) { + valid_cache_ids = append(valid_cache_ids, cache_id) + } else { + break + } + } + return valid_cache_ids +} + +/** helper functions for initializations of no standard types **/ + +// converts C type uint16[OCF_CORE_MAX] to Go slice []uint16 +// picks only valid cores and appends to result slice +func get_attached_cores(C_kcache_info *C.struct_kcas_cache_info) []uint16 { + var valid_core_ids []uint16 + + /** conversion C cores_id array to Golang slice */ + core_ids := (*[C.OCF_CORE_MAX]uint16)(unsafe.Pointer(&C_kcache_info.core_id[0]))[:C.OCF_CORE_MAX:C.OCF_CORE_MAX] + for core_idx := 0; core_idx < int(C_kcache_info.info.core_count); core_idx++ { + if valid_core_id(core_ids[core_idx]) { + valid_core_ids = append(valid_core_ids, core_ids[core_idx]) + } + } + return valid_core_ids +} + +/** checks for valid core id */ + +func valid_core_id(core_id uint16) bool { + return core_id < C.OCF_CORE_MAX +} + +/** checks for valid cache id */ + +func valid_cache_id(cache_id uint16) bool { + return C.OCF_CACHE_ID_MIN <= cache_id && cache_id < C.CACHE_LIST_ID_LIMIT +} + +/** checks if enum value as idx is out of range and returns valid value or default value */ + +func valid_enum_idx(idx int, enum_str_slice []string) string { + if 0 <= idx && idx < len(enum_str_slice) { + return enum_str_slice[idx] + } else { + return "undefined" + } +} diff --git a/json/ioctl/strut_conv.go b/json/ioctl/strut_conv.go deleted file mode 100644 index 789c891c0..000000000 --- a/json/ioctl/strut_conv.go +++ /dev/null @@ -1,202 +0,0 @@ -/* -* Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear -*/ - -package ioctl - -// #cgo CFLAGS: -I./../../modules/include/ -I/Ocf_env_headers.h -/* -#include -*/ -import "C" -import ( - "unsafe" -) - -/** kcas_get_stats struct conversion C to Go **/ - -func Conv_stats(C_kstats C.struct_kcas_get_stats) Kcas_get_stats { - res := Kcas_get_stats{ - Cache_id: uint16(C_kstats.cache_id), - Core_id: uint16(C_kstats.core_id), - IO_class_id: uint16(C_kstats.part_id), - Usage: Ocf_stats_usage{ - Occupancy: Ocf_stat{Value: uint64(C_kstats.usage.occupancy.value), - Fraction: uint64(C_kstats.usage.occupancy.fraction)}, - Free: Ocf_stat{Value: uint64(C_kstats.usage.free.value), - Fraction: uint64(C_kstats.usage.free.fraction)}, - Clean: Ocf_stat{Value: uint64(C_kstats.usage.clean.value), - Fraction: uint64(C_kstats.usage.clean.fraction)}, - Dirty: Ocf_stat{Value: uint64(C_kstats.usage.dirty.value), - Fraction: uint64(C_kstats.usage.dirty.fraction)}, - }, - Req: Ocf_stats_requests{ - Rd_hits: Ocf_stat{Value: uint64(C_kstats.req.rd_hits.value), - Fraction: uint64(C_kstats.req.rd_hits.fraction)}, - Rd_partial_misses: Ocf_stat{Value: uint64(C_kstats.req.rd_partial_misses.value), - Fraction: uint64(C_kstats.req.rd_partial_misses.fraction)}, - Rd_full_misses: Ocf_stat{Value: uint64(C_kstats.req.rd_full_misses.value), - Fraction: uint64(C_kstats.req.rd_full_misses.fraction)}, - Rd_total: Ocf_stat{Value: uint64(C_kstats.req.rd_total.value), - Fraction: uint64(C_kstats.req.rd_total.fraction)}, - Wr_hits: Ocf_stat{Value: uint64(C_kstats.req.wr_hits.value), - Fraction: uint64(C_kstats.req.wr_hits.fraction)}, - Wr_partial_misses: Ocf_stat{Value: uint64(C_kstats.req.wr_partial_misses.value), - Fraction: uint64(C_kstats.req.wr_partial_misses.fraction)}, - Wr_full_misses: Ocf_stat{Value: uint64(C_kstats.req.wr_full_misses.value), Fraction: uint64(C_kstats.req.wr_full_misses.fraction)}, - Wr_total: Ocf_stat{Value: uint64(C_kstats.req.wr_total.value), - Fraction: uint64(C_kstats.req.wr_total.fraction)}, - Rd_pt: Ocf_stat{Value: uint64(C_kstats.req.rd_pt.value), - Fraction: uint64(C_kstats.req.rd_pt.fraction)}, - Wr_pt: Ocf_stat{Value: uint64(C_kstats.req.wr_pt.value), - Fraction: uint64(C_kstats.req.wr_pt.fraction)}, - Serviced: Ocf_stat{Value: uint64(C_kstats.req.serviced.value), - Fraction: uint64(C_kstats.req.serviced.fraction)}, - Total: Ocf_stat{Value: uint64(C_kstats.req.total.value), - Fraction: uint64(C_kstats.req.total.fraction)}, - }, - Blocks: Ocf_stats_blocks{ - Core_volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_rd.value), - Fraction: uint64(C_kstats.blocks.core_volume_rd.fraction)}, - Core_volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_wr.value), - Fraction: uint64(C_kstats.blocks.core_volume_wr.fraction)}, - Core_volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.core_volume_total.value), - Fraction: uint64(C_kstats.blocks.core_volume_total.fraction)}, - Cache_volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_rd.value), - Fraction: uint64(C_kstats.blocks.cache_volume_rd.fraction)}, - Cache_volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_wr.value), - Fraction: uint64(C_kstats.blocks.cache_volume_wr.fraction)}, - Cache_volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.cache_volume_total.value), Fraction: uint64(C_kstats.blocks.cache_volume_total.fraction)}, - Volume_rd: Ocf_stat{Value: uint64(C_kstats.blocks.volume_rd.value), - Fraction: uint64(C_kstats.blocks.volume_rd.fraction)}, - Volume_wr: Ocf_stat{Value: uint64(C_kstats.blocks.volume_wr.value), - Fraction: uint64(C_kstats.blocks.volume_wr.fraction)}, - Volume_total: Ocf_stat{Value: uint64(C_kstats.blocks.volume_total.value), - Fraction: uint64(C_kstats.blocks.volume_total.fraction)}, - }, - Errors: Ocf_stats_errors{ - Core_volume_rd: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_rd.value), - Fraction: uint64(C_kstats.errors.core_volume_rd.fraction)}, - Core_volume_wr: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_wr.value), - Fraction: uint64(C_kstats.errors.core_volume_wr.fraction)}, - Core_volume_total: Ocf_stat{Value: uint64(C_kstats.errors.core_volume_total.value), - Fraction: uint64(C_kstats.errors.core_volume_total.fraction)}, - Cache_volume_rd: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_rd.value), - Fraction: uint64(C_kstats.errors.cache_volume_rd.fraction)}, - Cache_volume_wr: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_wr.value), - Fraction: uint64(C_kstats.errors.cache_volume_wr.fraction)}, - Cache_volume_total: Ocf_stat{Value: uint64(C_kstats.errors.cache_volume_total.value), - Fraction: uint64(C_kstats.errors.cache_volume_total.fraction)}, - Total: Ocf_stat{Value: uint64(C_kstats.errors.total.value), - Fraction: uint64(C_kstats.errors.total.fraction)}, - }, - } - return res -} - -/** cache_info struct conversion C to Go **/ - -func Conv_cache_info(C_cache_info C.struct_kcas_cache_info) Kcas_cache_info { - - var core_id []uint16 - for _, v := range (*[C.OCF_CORE_MAX]uint16)(unsafe.Pointer(&C_cache_info.core_id[0]))[:C.OCF_CORE_MAX:C.OCF_CORE_MAX] { - if v > 0 { - core_id = append(core_id, v) - } - } - var cache_mode string - if 0 <= C_cache_info.info.cache_mode { - cache_mode = ocf_cache_mode_str[C_cache_info.info.cache_mode] - } else { - cache_mode = "none" - } - - res := Kcas_cache_info{ - Cache_id: uint16(C_cache_info.cache_id), - Cache_path_name: C.GoString(&C_cache_info.cache_path_name[0]), - Core_id: core_id, - Info: Ocf_cache_info{ - Attached: bool(C_cache_info.info.attached), - Volume_type: uint8(C_cache_info.info.volume_type), - State: ocf_cache_state_str[C_cache_info.info.state], - Size: uint32(C_cache_info.info.size), - Inactive_cores: Core_stats{ - Occupancy: Ocf_stat{Value: uint64(C_cache_info.info.inactive.occupancy.value), - Fraction: uint64(C_cache_info.info.inactive.occupancy.fraction)}, - Clean: Ocf_stat{Value: uint64(C_cache_info.info.inactive.clean.value), - Fraction: uint64(C_cache_info.info.inactive.clean.fraction)}, - Dirty: Ocf_stat{Value: uint64(C_cache_info.info.inactive.dirty.value), - Fraction: uint64(C_cache_info.info.inactive.dirty.fraction)}, - }, - Occupancy: uint32(C_cache_info.info.occupancy), - Dirty: uint32(C_cache_info.info.dirty), - Dirty_for: uint64(C_cache_info.info.dirty_for), - Dirty_initial: uint32(C_cache_info.info.dirty_initial), - Cache_mode: cache_mode, - Fallback_pt: Fallback_pt_stats{ - Error_counter: int(C_cache_info.info.fallback_pt.error_counter), - Status: bool(C_cache_info.info.fallback_pt.status), - }, - Cleaning_policy: ocf_cleaning_str[C_cache_info.info.cleaning_policy], - Promotion_policy: ocf_promotion_str[C_cache_info.info.promotion_policy], - Cache_line_size: uint64(C_cache_info.info.cache_line_size), - Flushed: uint32(C_cache_info.info.flushed), - Core_count: uint32(C_cache_info.info.core_count), - Metadata_footprint: uint64(C_cache_info.info.metadata_footprint), - Metadata_end_offset: uint64(C_cache_info.info.metadata_end_offset), - }, - Metadata_mode: metadata_mode_str[C_cache_info.metadata_mode], - ext_err_code: int(C_cache_info.ext_err_code), - } - return res -} - -/** core_info struct conversion C to Go **/ - -func Conv_core_info(kcore_info C.struct_kcas_core_info) Kcas_core_info { - - res := Kcas_core_info{ - Core_path_name: C.GoString(&kcore_info.core_path_name[0]), - Cache_id: uint16(kcore_info.cache_id), - Core_id: uint16(kcore_info.core_id), - Info: Ocf_core_info{ - Core_size: uint64(kcore_info.info.core_size), - Core_size_bytes: uint64(kcore_info.info.core_size_bytes), - Flushed: uint32(kcore_info.info.flush_operation.flushed), - Dirty: uint32(kcore_info.info.flush_operation.dirty), - Dirty_for: uint64(kcore_info.info.dirty_for), - Seq_cutoff_threshold: uint32(kcore_info.info.seq_cutoff_threshold), - Seq_cutoff_policy: ocf_seq_cutoff_policy_str[kcore_info.info.seq_cutoff_policy], - }, - State: ocf_core_state_str[kcore_info.state], - } - return res -} - -/** io_class struct conversion C to Go **/ - -func Conv_io_class(kio_class C.struct_kcas_io_class) Kcas_io_class { - var cache_mode string - if 0 <= kio_class.info.cache_mode { - cache_mode = ocf_cache_mode_str[kio_class.info.cache_mode] - } else { - cache_mode = "none" - } - - res := Kcas_io_class{ - Cache_id: uint16(kio_class.cache_id), - Class_id: uint32(kio_class.class_id), - Info: Ocf_io_class_info{ - Name: C.GoString(&kio_class.info.name[0]), - Cache_mode: cache_mode, - Priority: int16(kio_class.info.priority), - Curr_size: uint32(kio_class.info.curr_size), - Min_size: int32(kio_class.info.min_size), - Max_size: int32(kio_class.info.max_size), - Cleaning_policy: ocf_cleaning_str[kio_class.info.cleaning_policy_type], - }, - ext_err_code: int(kio_class.ext_err_code), - } - return res -} diff --git a/json/main.go b/json/main.go index 62b9d70cd..6a964ac18 100644 --- a/json/main.go +++ b/json/main.go @@ -1,47 +1,16 @@ /* * Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-Clause-Clear -*/ +* SPDX-License-Identifier: BSD-Clause + */ /* - * Prototype of JSON RESTful API +* Prototype of JSON RESTful API */ package main -import ( - "fmt" - - "example.com/json/ioctl" - "example.com/json/rest" -) +import "github.com/Open-CAS/open-cas-linux/json/api" func main() { - fmt.Println("open-cas-linux-RestAPI") - - // test RestAPI - - // test request json file - var req rest.Request - req.Read_req() - req.Show_req() - req.Write_req() - - // get file descriptor - fd := ioctl.Read_fd() - - // JSON requests control - if req.Req_list_caches { - rest.List_caches(fd) - } - - if req.Req_get_stats { - rest.Get_stats(fd, req.Get_stats) - } - - // test cmd flags - //rest.RestAPI() - //rest.Flags() - /* - */ + api.Json_api() } diff --git a/json/request.json b/json/request.json index 37377c1c1..ff6425b54 100644 --- a/json/request.json +++ b/json/request.json @@ -1,12 +1,8 @@ { - "Requested list cache(s) and core(s)": false, - "Requested get statistics": true, - "Get statistics": { - "cache id": 1, - "Core id": 1, - "IO class": 0, - "Requested cache info": true, - "Requested core info": true, - "Requested io classinfo": true + "command": "opencas.cache.info.get", + "params": { + "cache id": 1 } -} \ No newline at end of file +} + + diff --git a/json/rest/compose_data.go b/json/rest/compose_data.go deleted file mode 100644 index e3fd8be95..000000000 --- a/json/rest/compose_data.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -* Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear -*/ - -package rest - -import ( - "fmt" - - "example.com/json/ioctl" -) - -func List_caches(fd uintptr) { - fmt.Println("\nLIST_CACHES_WITH_CORES\n") - - C_cache_info := ioctl.Ioctl_cache_info(fd, 1) - cache_info := ioctl.Conv_cache_info(C_cache_info) - ioctl.Marshal_kcache_info(cache_info) - - for _, v := range cache_info.Core_id { - C_kcore_info := ioctl.Ioctl_core_info(fd, cache_info.Cache_id, v) - kcore_info := ioctl.Conv_core_info(C_kcore_info) - ioctl.Marshal_kcore_info(kcore_info) - } -} - -func Get_stats(fd uintptr, req_get_stats Stats_info) { - fmt.Println("\nGET_STATS\n") - - if req_get_stats.Req_cache_info { - C_cache_info := ioctl.Ioctl_cache_info(fd, req_get_stats.Cache_id) - kcache_info := ioctl.Conv_cache_info(C_cache_info) - ioctl.Marshal_kcache_info(kcache_info) - } - - if req_get_stats.Req_core_info { - C_kcore_info := ioctl.Ioctl_core_info(fd, req_get_stats.Cache_id, req_get_stats.Core_id) - kcore_info := ioctl.Conv_core_info(C_kcore_info) - ioctl.Marshal_kcore_info(kcore_info) - } - - if req_get_stats.Req_io_class_info { - C_kio_class := ioctl.Ioctl_io_class(fd, req_get_stats.Cache_id, uint16(req_get_stats.Io_class)) - kio_class := ioctl.Conv_io_class(C_kio_class) - ioctl.Marshal_kio_class(kio_class) - } - - C_kstats := ioctl.Ioctl_get_kcas_stats(fd, req_get_stats.Cache_id, - req_get_stats.Core_id, uint16(req_get_stats.Io_class)) - kstats := ioctl.Conv_stats(C_kstats) - ioctl.Marshal_kcas_stats(kstats) -} diff --git a/json/rest/request.go b/json/rest/request.go deleted file mode 100644 index 143664fdf..000000000 --- a/json/rest/request.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -* Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear -*/ - -package rest - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" -) - -type Request struct { - Req_list_caches bool `json:"Requested list cache(s) and core(s)"` - Req_get_stats bool `json:"Requested get statistics"` - Get_stats Stats_info `json:"Get statistics"` -} - -type Stats_info struct { - Cache_id uint16 `json:"cache id"` - Core_id uint16 `json:"Core id"` - Io_class uint32 `json:"IO class"` - Req_cache_info bool `json:"Requested cache info"` - Req_core_info bool `json:"Requested core info"` - Req_io_class_info bool `json:"Requested io classinfo"` -} - -/** debug read and write json requests */ -func (req *Request) Write_req() { - file, err := json.MarshalIndent(req, "", " ") - _ = ioutil.WriteFile("request.json", file, 0644) - if err != nil { - log.Fatal(err) - } -} - -func (req *Request) Read_req() { - data, err := ioutil.ReadFile("request.json") - if err != nil { - log.Fatal(err) - } - json.Unmarshal(data, req) -} - -func (req *Request) Show_req() { - req_b, err := json.MarshalIndent(req, "", " ") - if err != nil { - log.Fatal(err) - } - fmt.Print("Request: ", string(req_b), "\n") -} diff --git a/json/rest/rest.go b/json/rest/rest.go deleted file mode 100644 index aeee0a08f..000000000 --- a/json/rest/rest.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -* Copyright(c) 2012-2021 Intel Corporation -* SPDX-License-Identifier: BSD-3-Clause-Clear -*/ - -package rest - -import ( - "encoding/json" - "fmt" - "log" - "net/http" -) - -func RestAPI() { - handleRequests() -} - -func homePage(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Open CAS - RestAPI") - fmt.Print("Endpoint hit: homePage") -} - -func handleRequests() { - http.HandleFunc("/", homePage) - - /** handling requests */ - http.HandleFunc("/request", returnRequest) - //http.HandleFunc("/get_stats", ) - //http.HandleFunc("/list_caches", ) - - log.Fatal(http.ListenAndServe(":10000", nil)) -} - -func returnRequest(w http.ResponseWriter, r *http.Request) { - fmt.Println("Endpoint hit: returnRequest") - var req Request - req.Read_req() - json.NewEncoder(w).Encode(req) -} From b0826e3f0d634b9061b4884204f45b7da80a9ff3 Mon Sep 17 00:00:00 2001 From: Piotr Debski Date: Thu, 23 Dec 2021 16:34:56 +0100 Subject: [PATCH 3/3] tests for JSON API Signed-off-by: Piotr Debski --- test/functional/api/cas/json_api.py | 60 +++++++++++++ test/functional/tests/stats/test_json_api.py | 95 ++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 test/functional/api/cas/json_api.py create mode 100644 test/functional/tests/stats/test_json_api.py diff --git a/test/functional/api/cas/json_api.py b/test/functional/api/cas/json_api.py new file mode 100644 index 000000000..5745226da --- /dev/null +++ b/test/functional/api/cas/json_api.py @@ -0,0 +1,60 @@ +# +# Copyright(c) 2019-2021 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause-Clear +# + +from core.test_run import TestRun +import json + + +class Response_structure: + response_keys = {"opencas.cache.stats.get": ["Cache id", "Cache id", "IO class", "Usage", + "Requests", "Blocks", "Errors"], + "opencas.cache.core.stats.get": ["Cache id", "Cache id", "IO class", "Usage", + "Requests", "Blocks", "Errors"], + "opencas.cache.ioclass.stats.get": ["Cache id", "Cache id", "IO class", + "Usage", "Requests", "Blocks", "Errors"], + "opencas.cache.core.ioclass.stats.get": ["Cache id", "Cache id", "IO class", + "Usage", "Requests", "Blocks", "Errors"], + "opencas.cache_list.get": [], + "opencas.cache.info.get": ["Cache id", "Cache device", "Core(s) id(s)", + "Cache details"], + "opencas.cache.core.info.get": ["Core id", "Core path", "Core details", + "State"], + "opencas.cache.ioclass.info.get": ["Class id", "IO class details"]} + + +class Json_api: + response_structure = Response_structure() + + @classmethod + def send_request(cls, request: dict): + request = json.dumps(request) + TestRun.LOGGER.info(f"Request: '{request}'") + exec_json_api_path = "./usr/sbin/opencas-json-api" + command = f"cd / && echo '{request}' | {exec_json_api_path}" + response = TestRun.executor.run(command) + if response.exit_code != 0: + raise Exception(f"Failed Request: '{request}'") + return response.stdout + + @classmethod + def verify_response_structure(cls, command: str, response: str): + response = json.loads(response) + for key in cls.response_structure.response_keys[command]: + if key not in response.keys(): + raise Exception(f"Response structure is not valid: missing {key} field") + TestRun.LOGGER.info(f"Request: {command} PASSED") + return True + + @classmethod + def verify_response_content(cls, response: str): + raise NotImplementedError() + + @classmethod + def check_json_api_installed(cls): + command = "ls /sbin/opencas-json-api" + output = TestRun.executor.run(command) + if output.exit_code != 0: + return True + return False diff --git a/test/functional/tests/stats/test_json_api.py b/test/functional/tests/stats/test_json_api.py new file mode 100644 index 000000000..fb70d1393 --- /dev/null +++ b/test/functional/tests/stats/test_json_api.py @@ -0,0 +1,95 @@ +# +# Copyright(c) 2019-2021 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause-Clear +# + +import pytest + +from api.cas import casadm +from api.cas.json_api import Json_api +from core.test_run import TestRun +from storage_devices.disk import DiskTypeSet, DiskType, DiskTypeLowerThan +from test_utils.size import Size, Unit + + +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane, DiskType.nand])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +def test_json_api_requests(): + with TestRun.step("Prepare CAS device."): + cache_disk = TestRun.disks['cache'] + cache_disk.create_partitions([Size(20, Unit.GibiByte)]) + cache_dev = cache_disk.partitions[0] + + core_disk = TestRun.disks['core'] + core_disk.create_partitions([Size(20, Unit.GibiByte)]) + core_dev = core_disk.partitions[0] + + cache = casadm.start_cache(cache_dev, force=True) + core = cache.add_core(core_dev) + TestRun.LOGGER.info(TestRun.executor.run("casadm -L").stdout) + + with TestRun.step("Parametrize Request valid"): + io_class = 0 + cache_id = cache.cache_id + core_id = core.core_id + + with TestRun.step("Corner values"): + io_class = 0 + cache_id_min = 1 + core_id_max = 0 + cache_id_max = 4096 + core_id_max = 16384 + + with TestRun.step("Init all requests"): + request_opencas_cache_stats_get = {"command": "opencas.cache.stats.get", + "params": {"cache id": cache_id}} + request_opencas_cache_core_stats_get = {"command": "opencas.cache.stats.get", + "params": {"cache id": cache_id, "core id": + core_id}} + request_opencas_cache_ioclass_stats_get = {"command": "opencas.cache.stats.get", + "params": {"cache id": cache_id, + "io class": io_class}} + request_opencas_cache_core_ioclass_stats_get = {"command": "opencas.cache.stats.get", + "params": {"cache id": cache_id, "core id": + core_id, "io class": io_class}} + request_opencas_cache_list_get = {"command": "opencas.cache_list.get", + "params": {}} + request_opencas_cache_info_get = {"command": "opencas.cache.info.get", + "params": {"cache id": cache_id, }} + request_opencas_core_info_get = {"command": "opencas.cache.core.info.get", + "params": {"cache id": cache_id, "core id": core_id}} + request_opencas_ioclass_info_get = {"command": "opencas.cache.ioclass.info.get", + "params": {"cache id": cache_id, "io class": io_class}} + + with TestRun.group("JSON API requests"): + with TestRun.step("GET CACHE REQUEST"): + response = Json_api.send_request(request_opencas_cache_stats_get) + Json_api.verify_response_structure(request_opencas_cache_stats_get["command"], response) + with TestRun.step("GET CACHE CORE REQUEST"): + response = Json_api.send_request(request_opencas_cache_core_stats_get) + Json_api.verify_response_structure( + request_opencas_cache_core_stats_get["command"], response) + with TestRun.step("GET CACHE IO CLASS REQUEST"): + response = Json_api.send_request(request_opencas_cache_ioclass_stats_get) + Json_api.verify_response_structure( + request_opencas_cache_ioclass_stats_get["command"], response) + with TestRun.step("GET CACHE CORE IO CLAS REQUEST"): + response = Json_api.send_request(request_opencas_cache_core_ioclass_stats_get) + Json_api.verify_response_structure( + request_opencas_cache_core_ioclass_stats_get["command"], response) + with TestRun.step("GET CACHE LIST"): + response = Json_api.send_request(request_opencas_cache_list_get) + Json_api.verify_response_structure(request_opencas_cache_list_get["command"], response) + with TestRun.step("GET CACHE INFO"): + response = Json_api.send_request(request_opencas_cache_info_get) + Json_api.verify_response_structure(request_opencas_cache_info_get["command"], response) + with TestRun.step("GET CORE INFO"): + response = Json_api.send_request(request_opencas_core_info_get) + Json_api.verify_response_structure(request_opencas_core_info_get["command"], response) + with TestRun.step("GET IO CLASS INFO"): + response = Json_api.send_request(request_opencas_ioclass_info_get) + Json_api.verify_response_structure( + request_opencas_ioclass_info_get["command"], response) + + with TestRun.group("JSON API invalid requests"): + pass