diff --git a/Makefile b/Makefile index d7f3db993be..4c8f9a355d3 100644 --- a/Makefile +++ b/Makefile @@ -207,6 +207,7 @@ install: build doc sdk doc-json install -D -m 755 _build/install/default/bin/xcp-rrdd-iostat $(DESTDIR)$(LIBEXECDIR)/xcp-rrdd-plugins/xcp-rrdd-iostat install -D -m 755 _build/install/default/bin/xcp-rrdd-squeezed $(DESTDIR)$(LIBEXECDIR)/xcp-rrdd-plugins/xcp-rrdd-squeezed install -D -m 755 _build/install/default/bin/xcp-rrdd-xenpm $(DESTDIR)$(LIBEXECDIR)/xcp-rrdd-plugins/xcp-rrdd-xenpm + install -D -m 755 _build/install/default/bin/xcp-rrdd-dcmi $(DESTDIR)$(LIBEXECDIR)/xcp-rrdd-plugins/xcp-rrdd-dcmi install -D -m 644 ocaml/xcp-rrdd/bugtool-plugin/rrdd-plugins.xml $(DESTDIR)$(ETCXENDIR)/bugtool/xcp-rrdd-plugins.xml install -D -m 644 ocaml/xcp-rrdd/bugtool-plugin/rrdd-plugins/stuff.xml $(DESTDIR)$(ETCXENDIR)/bugtool/xcp-rrdd-plugins/stuff.xml install -D -m 755 ocaml/xcp-rrdd/bin/rrdp-scripts/sysconfig-rrdd-plugins $(DESTDIR)/etc/sysconfig/xcp-rrdd-plugins diff --git a/ocaml/xcp-rrdd/bin/rrdp-dcmi/dune b/ocaml/xcp-rrdd/bin/rrdp-dcmi/dune new file mode 100644 index 00000000000..0f438a65861 --- /dev/null +++ b/ocaml/xcp-rrdd/bin/rrdp-dcmi/dune @@ -0,0 +1,16 @@ +(executable + (modes exe) + (name rrdp_dcmi) + (package rrdd-plugins) + (public_name xcp-rrdd-dcmi) + (libraries + dune-build-info + rrdd-plugin + rrdd-plugins.libs + xapi-idl.rrd + xapi-log + xapi-rrd + astring + ) +) + diff --git a/ocaml/xcp-rrdd/bin/rrdp-dcmi/rrdp_dcmi.ml b/ocaml/xcp-rrdd/bin/rrdp-dcmi/rrdp_dcmi.ml new file mode 100644 index 00000000000..6457924c22d --- /dev/null +++ b/ocaml/xcp-rrdd/bin/rrdp-dcmi/rrdp_dcmi.ml @@ -0,0 +1,90 @@ +(* + * Copyright (C) 2024 Cloud Software Group, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +(** Read power measurements from IPMI DCMI where available. + There is also IPMI SDR entity 21 that returns the same information (power consumption in watts), + but isn't always available, and seems to be slower to read, especially when missing. + *) + +open Rrdd_plugin + +module Process = Process (struct let name = "xcp-rrdd-dcmi" end) + +open Process + +let ipmitool_bin = "/usr/bin/ipmitool" + +let ipmitool args = + (* we connect to the local /dev/ipmi0 if available to read measurements from local BMC *) + (ipmitool_bin :: "-I" :: "open" :: args) |> String.concat " " + +let discover () = + Utils.exec_cmd + (module Process.D) + ~cmdstring:(ipmitool ["dcmi"; "discover"]) + ~f:(fun line -> + D.debug "DCMI discover: %s" line ; + if String.trim line = "Power management available" then + Some true + else + None + ) + +let ( let+ ) x f = Option.map f x + +let ( let* ) = Option.bind + +let get_dcmi_power_reading () = + Utils.exec_cmd + (module Process.D) + ~cmdstring:(ipmitool ["dcmi"; "power"; "reading"]) + ~f:(fun line -> + let* k, v = line |> Astring.String.cut ~sep:":" in + let k = String.trim k and v = String.trim v in + if k = "Instantaneous power reading" then + let* watts, units = Astring.String.cut ~sep:" " v in + if units = "Watts" then + Some (Int64.of_string watts) + else + None + else + None + ) + +let gen_dcmi_power_reading value = + ( Rrd.Host + , Ds.ds_make ~name:"DCMI-power-reading" + ~description:"Host power usage reported by IPMI DCMI" + ~value:(Rrd.VT_Int64 value) ~ty:Rrd.Gauge ~default:true ~units:"W" ~min:0. + () + ) + +let generate_dss () = + match get_dcmi_power_reading () with + | watts :: _ -> + [gen_dcmi_power_reading watts] + | _ -> + [] + +let _ = + initialise () ; + (* D.log_to_stdout () *) + match discover () with + | [] -> + D.info "IPMI DCMI power reading is unavailable"; + exit 1 + | _ -> + D.info "IPMI DCMI power reading is available"; + main_loop ~neg_shift:0.5 ~target:(Reporter.Local 1) + ~protocol:Rrd_interface.V2 ~dss_f:generate_dss diff --git a/ocaml/xcp-rrdd/bin/rrdp-scripts/sysconfig-rrdd-plugins b/ocaml/xcp-rrdd/bin/rrdp-scripts/sysconfig-rrdd-plugins index 6998ad20c12..ced7c537254 100644 --- a/ocaml/xcp-rrdd/bin/rrdp-scripts/sysconfig-rrdd-plugins +++ b/ocaml/xcp-rrdd/bin/rrdp-scripts/sysconfig-rrdd-plugins @@ -1 +1 @@ -PLUGINS="xcp-rrdd-iostat xcp-rrdd-squeezed xcp-rrdd-xenpm" +PLUGINS="xcp-rrdd-iostat xcp-rrdd-squeezed xcp-rrdd-xenpm xcp-rrdd-dcmi"